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