/src/httrack/src/htsarena.h
Line | Count | Source |
1 | | /* ------------------------------------------------------------ */ |
2 | | /* |
3 | | HTTrack Website Copier, Offline Browser for Windows and Unix |
4 | | Copyright (C) 1998 Xavier Roche and other contributors |
5 | | |
6 | | SPDX-License-Identifier: GPL-3.0-or-later |
7 | | |
8 | | This program is free software: you can redistribute it and/or modify |
9 | | it under the terms of the GNU General Public License as published by |
10 | | the Free Software Foundation, either version 3 of the License, or |
11 | | (at your option) any later version. |
12 | | |
13 | | This program 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 |
16 | | GNU General Public License for more details. |
17 | | |
18 | | You should have received a copy of the GNU General Public License |
19 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 | | |
21 | | Please visit our Website: http://www.httrack.com |
22 | | */ |
23 | | |
24 | | /* ------------------------------------------------------------ */ |
25 | | /* File: htsarena.h bump allocator whose allocations never move */ |
26 | | /* Author: Xavier Roche */ |
27 | | /* ------------------------------------------------------------ */ |
28 | | |
29 | | #ifndef HTSARENA_DEFH |
30 | | #define HTSARENA_DEFH |
31 | | |
32 | | #include "htsglobal.h" |
33 | | #include "htssafe.h" |
34 | | |
35 | | #include <string.h> |
36 | | |
37 | | /** @file htsarena.h |
38 | | * Bump allocator over chunks that are never resized, so what it hands out |
39 | | * keeps its address, which the realloc-based String and TypedArray cannot |
40 | | * promise. Nothing is released until the whole arena is. **/ |
41 | | |
42 | | /** Alignment fit for anything an arena is asked to hold. **/ |
43 | | typedef union { |
44 | | void *p; |
45 | | long l; |
46 | | double d; |
47 | | } hts_arena_align; |
48 | | |
49 | | typedef struct hts_arena_chunk hts_arena_chunk; |
50 | | |
51 | | struct hts_arena_chunk { |
52 | | hts_arena_chunk *next; |
53 | | }; |
54 | | |
55 | | typedef struct { |
56 | | hts_arena_chunk *chunks; /* newest first; allocations are cut from it */ |
57 | | size_t size; /* the newest chunk, used of size bytes taken */ |
58 | | size_t used; |
59 | | } hts_arena; |
60 | | |
61 | 0 | #define HTS_ARENA_ALIGN sizeof(hts_arena_align) |
62 | | |
63 | | /** Smallest chunk, then doubling up to the largest. **/ |
64 | 0 | #define HTS_ARENA_MIN 32768 |
65 | 0 | #define HTS_ARENA_MAX (16 * 1024 * 1024) |
66 | | |
67 | | /** The padding below assumes a power of two, and a header that is already a |
68 | | multiple of it; neither can change without this failing to compile. **/ |
69 | | HTS_STATIC_ASSERT(!(HTS_ARENA_ALIGN & (HTS_ARENA_ALIGN - 1)), arena_align_pow2); |
70 | | |
71 | | /** First aligned offset past the chunk header, where its bytes begin. **/ |
72 | | #define HTS_ARENA_HDR \ |
73 | 0 | ((sizeof(hts_arena_chunk) + HTS_ARENA_ALIGN - 1) & \ |
74 | 0 | ~(size_t) (HTS_ARENA_ALIGN - 1)) |
75 | | |
76 | | /** Take a chunk with room for NEED bytes; ARENA is unchanged on failure. **/ |
77 | 0 | static HTS_UNUSED hts_boolean hts_arena_grow_(hts_arena *arena, size_t need) { |
78 | 0 | size_t size = arena->size < HTS_ARENA_MAX / 2 ? arena->size * 2 |
79 | 0 | : (size_t) HTS_ARENA_MAX; |
80 | 0 | hts_arena_chunk *chunk; |
81 | |
|
82 | 0 | if (size < HTS_ARENA_MIN) |
83 | 0 | size = HTS_ARENA_MIN; |
84 | 0 | if (size < need) |
85 | 0 | size = need; |
86 | 0 | if (size > (size_t) -1 - HTS_ARENA_HDR) |
87 | 0 | return HTS_FALSE; |
88 | 0 | chunk = (hts_arena_chunk *) malloct(HTS_ARENA_HDR + size); |
89 | 0 | if (chunk == NULL) |
90 | 0 | return HTS_FALSE; |
91 | 0 | chunk->next = arena->chunks; |
92 | 0 | arena->chunks = chunk; |
93 | 0 | arena->size = size; |
94 | 0 | arena->used = 0; |
95 | 0 | return HTS_TRUE; |
96 | 0 | } |
97 | | |
98 | | HTS_STATIC_ASSERT(HTS_ARENA_HDR % HTS_ARENA_ALIGN == 0, arena_hdr_aligned); |
99 | | |
100 | | /** SIZE bytes from ARENA, starting on an ALIGN boundary (a power of two), or |
101 | | NULL when out of memory. Padding is charged where it is needed rather than |
102 | | to every size, so a run of strings stays packed. **/ |
103 | | static HTS_UNUSED void *hts_arena_take_(hts_arena *arena, size_t size, |
104 | 0 | size_t align) { |
105 | 0 | if (arena->chunks != NULL) { |
106 | | /* used never passes size, so the room below can not go negative */ |
107 | 0 | const size_t room = arena->size - arena->used; |
108 | 0 | const size_t pad = (align - (arena->used & (align - 1))) & (align - 1); |
109 | | |
110 | | /* pad, then size, each alone on one side: neither test can wrap */ |
111 | 0 | if (pad <= room && size <= room - pad) { |
112 | 0 | char *const at = |
113 | 0 | (char *) arena->chunks + HTS_ARENA_HDR + arena->used + pad; |
114 | |
|
115 | 0 | arena->used += pad + size; |
116 | 0 | return at; |
117 | 0 | } |
118 | 0 | } |
119 | | /* a fresh chunk begins aligned, so it needs no padding */ |
120 | 0 | if (!hts_arena_grow_(arena, size)) |
121 | 0 | return NULL; |
122 | 0 | arena->used = size; |
123 | 0 | return (char *) arena->chunks + HTS_ARENA_HDR; |
124 | 0 | } |
125 | | |
126 | | /** SIZE bytes from ARENA, aligned for any type, or NULL when out of memory. |
127 | | The address stays valid until hts_arena_free(). **/ |
128 | 0 | static HTS_UNUSED void *hts_arena_alloc(hts_arena *arena, size_t size) { |
129 | 0 | return hts_arena_take_(arena, size, HTS_ARENA_ALIGN); |
130 | 0 | } |
131 | | |
132 | | /** A copy of S in ARENA, or NULL when out of memory. **/ |
133 | 0 | static HTS_UNUSED char *hts_arena_strdup(hts_arena *arena, const char *s) { |
134 | 0 | const size_t len = strlen(s) + 1; |
135 | 0 | char *const copy = (char *) hts_arena_take_(arena, len, 1); |
136 | |
|
137 | 0 | if (copy != NULL) |
138 | 0 | memcpy(copy, s, len); |
139 | 0 | return copy; |
140 | 0 | } |
141 | | |
142 | | /** Release every chunk, and with them everything the arena handed out. **/ |
143 | 0 | static HTS_UNUSED void hts_arena_free(hts_arena *arena) { |
144 | 0 | hts_arena_chunk *chunk = arena->chunks; |
145 | |
|
146 | 0 | while (chunk != NULL) { |
147 | 0 | hts_arena_chunk *const next = chunk->next; |
148 | |
|
149 | | freet(chunk); |
150 | 0 | chunk = next; |
151 | 0 | } |
152 | 0 | memset(arena, 0, sizeof(*arena)); |
153 | 0 | } |
154 | | |
155 | | #endif |