1 /* Jitter: dynamic buffer data structure.
2
3 Copyright (C) 2017, 2018, 2019, 2020 Luca Saiu
4 Written by Luca Saiu
5
6 This file is part of Jitter.
7
8 Jitter 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 Jitter 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 Jitter. If not, see <http://www.gnu.org/licenses/>. */
20
21
22 #include <stdio.h>
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "jitter.h"
28
29 #include "jitter-malloc.h"
30 #include "jitter-dynamic-buffer.h"
31
32
33 /* Initial memory size.
34 * ************************************************************************** */
35
36 /* Buffer sizes are all stored in chars, to make pointer arithmetic easier. */
37 #define INITIAL_ALLOCATED_SIZE 256
38
39
40
41
42 /* Initialization and finalization.
43 * ************************************************************************** */
44
45 void
jitter_dynamic_buffer_initialize_with_allocated_size(struct jitter_dynamic_buffer * da,size_t initial_allocated_size)46 jitter_dynamic_buffer_initialize_with_allocated_size
47 (struct jitter_dynamic_buffer *da, size_t initial_allocated_size)
48 {
49 /* Allocate a region with the given initial size, keeping track of how much
50 memory is available and how much is already used. */
51 da->allocated_size = initial_allocated_size;
52 da->used_size = 0;
53 da->region = jitter_xmalloc (initial_allocated_size);
54 }
55
56 void
jitter_dynamic_buffer_initialize(struct jitter_dynamic_buffer * da)57 jitter_dynamic_buffer_initialize (struct jitter_dynamic_buffer *da)
58 {
59 jitter_dynamic_buffer_initialize_with_allocated_size
60 (da, INITIAL_ALLOCATED_SIZE);
61 }
62
63 void
jitter_dynamic_buffer_finalize(struct jitter_dynamic_buffer * da)64 jitter_dynamic_buffer_finalize (struct jitter_dynamic_buffer *da)
65 {
66 /* Do nothing is the region has been extracted. */
67 if (da->region == NULL)
68 return;
69
70 /* Free the malloc-allocated part. */
71 free (da->region);
72
73 /* For defensiveness, fill the struct with invalid data. */
74 memset (da, 0xff, sizeof (struct jitter_dynamic_buffer));
75 }
76
77
78
79
80 /* Reserving or releasing memory.
81 * ************************************************************************** */
82
83 void *
jitter_dynamic_buffer_reserve(struct jitter_dynamic_buffer * db,size_t chars_to_reserve)84 jitter_dynamic_buffer_reserve (struct jitter_dynamic_buffer *db,
85 size_t chars_to_reserve)
86 {
87 /* Remember the offset of the reserved space from the region beginning. */
88 size_t offset = db->used_size;
89
90 /* Reserve space, which possibly entails reallocating the region. This
91 potentially changes the region pointer, which is why we can compute the
92 result only after this.
93 Doubling *and adding one* is reasonable, as it lets us start with a zero
94 size as well, without needing any more conditionals. */
95 db->used_size += chars_to_reserve;
96 if (db->used_size > db->allocated_size)
97 db->region = jitter_xrealloc (db->region,
98 db->allocated_size = db->used_size * 2 + 1);
99
100 /* Now we know where the reserved space begins: it comes offset chars after
101 the region, as it is now. */
102 return db->region + offset;
103 }
104
105 void *
jitter_dynamic_buffer_push(struct jitter_dynamic_buffer * db,const void * new_element,size_t new_element_size_in_chars)106 jitter_dynamic_buffer_push (struct jitter_dynamic_buffer *db,
107 const void *new_element,
108 size_t new_element_size_in_chars)
109 {
110 /* Reserve space, and remember where the new space begins. */
111 void *res = jitter_dynamic_buffer_reserve (db, new_element_size_in_chars);
112
113 /* Fill the space. */
114 memcpy (res, new_element, new_element_size_in_chars);
115
116 /* Return the beginning of the copy. */
117 return res;
118 }
119
120 const void *
jitter_dynamic_buffer_push_const(struct jitter_dynamic_buffer * db,const void * new_element,size_t new_element_size_in_chars)121 jitter_dynamic_buffer_push_const (struct jitter_dynamic_buffer *db,
122 const void *new_element,
123 size_t new_element_size_in_chars)
124 {
125 return jitter_dynamic_buffer_push (db, new_element,
126 new_element_size_in_chars);
127 }
128
129 void*
jitter_dynamic_buffer_pop(struct jitter_dynamic_buffer * db,size_t chars_to_release)130 jitter_dynamic_buffer_pop (struct jitter_dynamic_buffer *db,
131 size_t chars_to_release)
132 {
133 assert (db->used_size >= chars_to_release);
134 db->used_size -= chars_to_release;
135 return db->region + db->used_size;
136 }
137
138 const void*
jitter_dynamic_buffer_pop_const(struct jitter_dynamic_buffer * db,size_t chars_to_release)139 jitter_dynamic_buffer_pop_const (struct jitter_dynamic_buffer *db,
140 size_t chars_to_release)
141 {
142 return jitter_dynamic_buffer_pop (db, chars_to_release);
143 }
144
145 void*
jitter_dynamic_buffer_first_unused_char(const struct jitter_dynamic_buffer * db)146 jitter_dynamic_buffer_first_unused_char (const struct jitter_dynamic_buffer *db)
147 {
148 return db->region + db->used_size;
149 }
150
151 const void*
jitter_dynamic_buffer_first_unused_char_const(const struct jitter_dynamic_buffer * db)152 jitter_dynamic_buffer_first_unused_char_const
153 (const struct jitter_dynamic_buffer *db)
154 {
155 return jitter_dynamic_buffer_first_unused_char (db);
156 }
157
158 size_t
jitter_dynamic_buffer_size(const struct jitter_dynamic_buffer * db)159 jitter_dynamic_buffer_size (const struct jitter_dynamic_buffer *db)
160 {
161 return db->used_size;
162 }
163
164 void
jitter_dynamic_buffer_compact(struct jitter_dynamic_buffer * db,size_t wiggle_byte_no)165 jitter_dynamic_buffer_compact (struct jitter_dynamic_buffer *db,
166 size_t wiggle_byte_no)
167 {
168 /* Do nothing if there are less than wiggle_byte_no bytes free. */
169 if (db->allocated_size - db->used_size <= wiggle_byte_no)
170 return;
171
172 /* If we arrived here then the size is actually shrinking. */
173
174 /* Make the allocated size equal to the minimum possible, which is to say the
175 number of bytes currently in use, plus the wiggle space. Then resize the
176 buffer. jitter_xmalloc is intended to also supports a size of zero. */
177 db->allocated_size = db->used_size + wiggle_byte_no;
178 db->region = jitter_xrealloc (db->region, db->allocated_size);
179 }
180
181
182
183
184 /* Conversion to an ordinary pointer.
185 * ************************************************************************** */
186
187 void*
jitter_dynamic_buffer_to_pointer(const struct jitter_dynamic_buffer * db)188 jitter_dynamic_buffer_to_pointer (const struct jitter_dynamic_buffer *db)
189 {
190 return db->region;
191 }
192
193 const void*
jitter_dynamic_buffer_to_const_pointer(const struct jitter_dynamic_buffer * db)194 jitter_dynamic_buffer_to_const_pointer (const struct jitter_dynamic_buffer *db)
195 {
196 return jitter_dynamic_buffer_to_pointer (db);
197 }
198
199
200
201
202 /* Extraction.
203 * ************************************************************************** */
204
205 void *
jitter_dynamic_buffer_extract(struct jitter_dynamic_buffer * db)206 jitter_dynamic_buffer_extract (struct jitter_dynamic_buffer *db)
207 {
208 /* Return the pointed buffer, and mark the region is invalidated. */
209 void *res = db->region;
210 db->region = NULL;
211 return res;
212 }
213
214 void *
jitter_dynamic_buffer_extract_trimmed(struct jitter_dynamic_buffer * db)215 jitter_dynamic_buffer_extract_trimmed (struct jitter_dynamic_buffer *db)
216 {
217 /* Extract without trimming, then realloc the result. */
218 void *untrimmed_res = jitter_dynamic_buffer_extract (db);
219 return jitter_xrealloc (untrimmed_res, db->used_size);
220 }
221