1 /****************************************************************************
2  * Copyright (C) 2008 by Matteo Franchin                                    *
3  *                                                                          *
4  * This file is part of Box.                                                *
5  *                                                                          *
6  *   Box is free software: you can redistribute it and/or modify it         *
7  *   under the terms of the GNU Lesser General Public License as published  *
8  *   by the Free Software Foundation, either version 3 of the License, or   *
9  *   (at your option) any later version.                                    *
10  *                                                                          *
11  *   Box is distributed in the hope that it will be useful,                 *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
14  *   GNU Lesser General Public License for more details.                    *
15  *                                                                          *
16  *   You should have received a copy of the GNU Lesser General Public       *
17  *   License along with Box.  If not, see <http://www.gnu.org/licenses/>.   *
18  ****************************************************************************/
19 
20 /* buffer.c - aprile 2002
21  *
22  * Questo file contiene il codice necessario per gestire un buffer.
23  * Si possono inserire e togliere elementi senza preoccuparsi
24  * dei ridimensionamenti del buffer.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 /* De-commentare per includere i messaggi di debug */
32 /*#define DEBUG*/
33 
34 #include "error.h"
35 #include "buffer.h"
36 
37 /* Questa macro provvede ad allargare il buffer
38  * in modo che riesca a contenere n elementi
39  */
40 #define BUFF_EXPAND(buffer, n) \
41   if ( n > buffer->dim ) { \
42     if ( buffer->dim == 0 ) { \
43       for( buffer->dim = buffer->mindim; \
44        buffer->dim < n; buffer->dim *= 2); \
45       buffer->size = (buffer->dim) * buffer->elsize; \
46       buffer->ptr = malloc( buffer->size ); \
47     } else { \
48       for(; buffer->dim < n; buffer->dim *= 2); \
49       buffer->size = (buffer->dim)*buffer->elsize; \
50       buffer->ptr = realloc( buffer->ptr, buffer->size ); \
51     } \
52     if ( buffer->ptr == NULL ) { \
53       FATALMSG("buffer.c", "Memoria esaurita"); \
54       return 0; \
55     }\
56     }
57 
58 /* Questa macro provvede a contrarre il buffer
59  * qualora sia vuoto per piu' di 3/4
60  * in modo tale che sia almeno mezzo pieno.
61  * n e' il numero di elementi del buffer.
62  */
63 #define BUFF_SHRINK(buffer, n) \
64   if (buffer->dim > buffer->mindim) { \
65     for(; 4*n < buffer->dim; buffer->dim /= 2); \
66     if (buffer->dim < buffer->mindim) \
67       buffer->dim = buffer->mindim; \
68     buffer->size = (buffer->dim)*buffer->elsize; \
69     buffer->ptr = realloc( buffer->ptr, buffer->size ); \
70     if ( buffer->ptr == NULL ) { \
71       ERRORMSG("buffer.c", "Problemi con realloc"); \
72       return 0; \
73     } \
74     }
75 
76 /* NOME: buff_create
77  * DESCRIZIONE: Crea un buffer con:
78  *  elsize = numero byte per elemento
79  *  mindim = dimensione minima del buffer (in elementi)
80  *  Restituisce 1 se tutto va bene, 0 in caso di errore.
81  * NOTA: buffer e' un puntatore ad una struttura di tipo
82  *  buff (gia' precedentemente allocata).
83  */
buff_create(buff * buffer,short elsize,long mindim)84 int buff_create(buff *buffer, short elsize, long mindim) {
85   if (elsize<1 || mindim<1) {
86     ERRORMSG("buff_create", "Parametri errati");
87     buffer->id = 0;
88     return 0;
89   }
90 
91   buffer->id = buffID;
92   buffer->ptr = NULL;
93   buffer->dim = 0;
94   buffer->size = 0;
95   buffer->numel = 0;
96   buffer->mindim = mindim;
97   buffer->elsize = elsize;
98 
99   return 1;
100 }
101 
buff_dup(buff * dest,buff * src)102 int buff_dup(buff *dest, buff *src) {
103   if (src->id != buffID) {
104     ERRORMSG("buff_recycle", "Buffer danneggiato");
105     return 0;
106   }
107 
108   *dest = *src;
109   if (dest->size > 0 && dest->ptr != (void *) NULL) {
110     dest->ptr = (void *) malloc(dest->size);
111     (void) memcpy(dest->ptr, src->ptr, dest->size);
112   }
113   return 1;
114 }
115 
116 /* NOME: buff_create
117  * DESCRIZIONE: Converte il buffer specificato, in un nuovo buffer
118  *  con elementi di dimensione diversa (elsize).
119  *  mindim = dimensione minima del buffer (in elementi)
120  *  Restituisce 1 se tutto va bene, 0 in caso di errore.
121  * NOTA: Il buffer creato sara' vuoto.
122  */
buff_recycle(buff * buffer,short elsize,long mindim)123 int buff_recycle(buff *buffer, short elsize, long mindim) {
124   if ( (elsize<1) || (mindim<1) ) {
125     ERRORMSG("buff_recycle", "Parametri errati");
126     return 0;
127   }
128 
129   if ( buffer->id != buffID ) {
130     ERRORMSG("buff_recycle", "Buffer danneggiato");
131     return 0;
132   }
133 
134   /* Calcolo quanti elementi di dimensione elsize ci stanno nella zona
135    * allocata per il vecchio buffer
136    */
137   buffer->dim = buffer->size / elsize;
138   if ( buffer->dim < 1 ) {
139     /* Non c'e' memoria sufficiente:
140      * distruggo il vecchio buffer e ne creo uno nuovo!
141      */
142     free( buffer->ptr );
143     buffer->id = 0;
144 
145     return buff_create(buffer, elsize, mindim);
146   }
147 
148   buffer->numel = 0;
149   buffer->mindim = mindim;
150   buffer->elsize = elsize;
151   return 1;
152 }
153 
154 /* NOME: buff_push
155  * DESCRIZIONE: Inserisce un elemento in coda nel buffer,
156  *  preoccupandosi di reallocare la memoria se necessario.
157  *  Restituisce 1 solo in caso di successo.
158  */
buff_push(buff * buffer,void * elem)159 int buff_push(buff *buffer, void *elem) {
160   void *tptr;
161   long tpos;
162 
163   if (buffer->id == buffID) {
164     tpos = buffer->numel++ * buffer->elsize;
165 
166     /* Controlla che il buffer non debba essere allargato */
167     BUFF_EXPAND(buffer, buffer->numel);
168 
169     tptr = buffer->ptr + tpos;
170     memcpy(tptr, elem, buffer->elsize);
171 
172   } else {
173     ERRORMSG("buff_push", "Buffer non inizializzato");
174     return 0;
175   }
176 
177   return 1;
178 }
179 
180 /* NOME: buff_mpush
181  * DESCRIZIONE: Inserisce pi elementi in coda nel buffer,
182  *  preoccupandosi di reallocare la memoria se necessario.
183  *  Restituisce 1 solo in caso di successo.
184  */
buff_mpush(buff * buffer,void * elem,long numel)185 int buff_mpush(buff *buffer, void *elem, long numel) {
186   void *tptr;
187   long tpos;
188 
189   if (buffer->id == buffID) {
190     if (numel < 1) {
191       ERRORMSG("buff_mpush", "Parametri errati");
192       return 0;
193     }
194 
195     tpos = buffer->numel * buffer->elsize;
196     buffer->numel += numel;
197 
198     /* Controlla che il buffer non debba essere allargato */
199     BUFF_EXPAND(buffer, buffer->numel);
200 
201     tptr = buffer->ptr + tpos;
202     memcpy(tptr, elem, numel*buffer->elsize);
203 
204   } else {
205     ERRORMSG("buff_mpush", "Buffer non inizializzato");
206     return 0;
207   }
208 
209   return 1;
210 }
211 
212 /* NOME: buff_bigenough
213  * DESCRIZIONE: Se necessario allarga il buffer in modo che contenga
214  *  numel elementi. Restituisce 1 in caso di successo.
215  */
buff_bigenough(buff * buffer,long numel)216 int buff_bigenough(buff *buffer, long numel) {
217   if (buffer->id == buffID) {
218     if (numel < 0) {
219       ERRORMSG("buff_bigenough", "Parametri errati");
220       return 0;
221     }
222 
223     /* Controlla che il buffer non debba essere allargato */
224     BUFF_EXPAND(buffer, numel);
225 
226   } else {
227     ERRORMSG("buff_bigenough", "Buffer non inizializzato");
228     return 0;
229   }
230 
231   return 1;
232 }
233 
234 /* NOME: buff_smallenough
235  * DESCRIZIONE: Se necessario riduce il buffer in modo che non sia
236  *  troppo vuoto. Restituisce 1 in caso di successo.
237  */
buff_smallenough(buff * buffer,long numel)238 int buff_smallenough(buff *buffer, long numel) {
239   if (buffer->id == buffID) {
240     if (numel < 0) {
241       ERRORMSG("buff_smallenough", "Parametri errati");
242       return 0;
243     }
244 
245     /* Controlla che il buffer non debba essere allargato */
246     BUFF_SHRINK(buffer, numel);
247 
248   } else {
249     ERRORMSG("buff_smallenough", "Buffer non inizializzato");
250     return 0;
251   }
252 
253   return 1;
254 }
255 
256 /* NOME: buff_clear
257  * DESCRIZIONE: Svuota il buffer, riallocandolo se la sua dimensione
258  *  supera la dimensione "di riposo".
259  *  Restituisce 1 in caso di successo.
260  */
buff_clear(buff * buffer)261 int buff_clear(buff *buffer) {
262   if (buff_smallenough(buffer, 0))
263     buffer->numel = 0;
264   else
265     return 0;
266 
267   return 1;
268 }
269 
270 /* NOME: buff_free
271  * DESCRIZIONE: De-alloca il buffer, riportandolo alla condizione in cui
272  *  buff_create lo aveva posto.
273  */
buff_free(buff * buffer)274 void buff_free(buff *buffer) {
275   if (buffer->id == buffID) {
276     free(buffer->ptr);
277     buffer->ptr = NULL;
278     buffer->dim = 0;
279     buffer->size = 0;
280     buffer->numel = 0;
281   }
282 }
283