1 #include <string.h>
  2 #include <stdlib.h>
  3 #include <errno.h>
  4 #include <stdio.h>
  5 #include <sys/sendfile.h>
  6 #include <sys/uio.h>
  7 #include "rh_bitset.h"
  8 #include "rh_chunk.h"
  9 
 10 static int rh_chunk_write_iovec (rh_chunk_t *chunk, int fd,
 11                 struct rh_chunk_entry *entry);
 12 
 13 static int rh_chunk_write_file (rh_chunk_t *chunk, int fd,
 14                 struct rh_chunk_entry *entry);
 15 
 16 static void rh_chunk_destroy_iovec (rh_chunk_t *chunk,
 17                 struct rh_chunk_entry *entry);
 18 
 19 int rh_chunk_done (rh_chunk_t *chunk)
 20 {
 21         return (NULL == TAILQ_FIRST(&chunk->list));
 22 }
 23 
 24 void rh_chunk_init (rh_chunk_t *chunk)
 25 {
 26         memset (chunk, 0, sizeof(*chunk));
 27 
 28         TAILQ_INIT(&chunk->list);
 29 }
 30 
 31 void rh_chunk_destroy (rh_chunk_t *chunk)
 32 {
 33         for (;;) {
 34                 struct rh_chunk_entry           *entry;
 35                 struct rh_chunk_entry_file      *file;
 36 
 37                 entry = TAILQ_FIRST(&chunk->list);
 38                 if (NULL == entry)
 39                         break;
 40 
 41                 switch (entry->type) {
 42                 case RH_CHUNK_ENTRY_IOVEC:
 43                         rh_chunk_destroy_iovec (chunk, entry);
 44                         break;
 45                 
 46                 case RH_CHUNK_ENTRY_FILE:
 47                         file = entry->data.file;
 48 
 49                         TAILQ_REMOVE(&chunk->list, entry, list);
 50 
 51                         if (file->cb && file->cb_arg)
 52                                 file->cb(file->cb_arg);
 53         
 54                         free (file);
 55                         free (entry);
 56                         
 57                         break;
 58         
 59                 default:
 60                         
 61                         TAILQ_REMOVE(&chunk->list, entry, list);
 62                         free (entry);
 63                         
 64                         break;
 65                 }
 66         }
 67 }
 68 
 69 void rh_chunk_reset (rh_chunk_t *chunk)
 70 {
 71         rh_chunk_destroy (chunk);
 72         rh_chunk_init    (chunk);
 73 }
 74 
 75 ssize_t rh_chunk_write (rh_chunk_t *chunk, int fd)
 76 {
 77         size_t bytes;
 78 
 79         bytes = chunk->size_out;
 80         
 81         for (;;) {
 82                 struct rh_chunk_entry   *entry;
 83 
 84                 entry = TAILQ_FIRST(&chunk->list);
 85                 if (NULL == entry)
 86                         break;
 87                 
 88                 switch (entry->type) {
 89                 case RH_CHUNK_ENTRY_UNKNOWN:
 90                         break;
 91 
 92                 case RH_CHUNK_ENTRY_IOVEC:
 93                         if (0 != rh_chunk_write_iovec (chunk, fd, entry))
 94                                 return -1;
 95                                 
 96                         break;
 97                 case RH_CHUNK_ENTRY_FILE:
 98                         if (0 != rh_chunk_write_file (chunk, fd, entry))
 99                                 return -1;
100                         break;
101                 }
102         
103         }
104         
105         return (chunk->size_out - bytes);
106 }
107 
108 int rh_chunk_append_uint (struct rh_chunk *chunk, size_t len)
109 {
110         char    *len_str, *ptr;
111         int     len_nstr;
112 
113         len_str = malloc(32);
114         if (NULL == len_str)
115                 return -1;
116 
117         ptr = len_str + 31;
118         if (0 == len) {
119                 *ptr-- = '0';
120         } else {
121                 for (; len; len/=10)
122                         *ptr-- = '0' + (len % 10);
123         }
124 
125         len_nstr = (len_str + 31) - ptr;
126 
127         if (-1 == rh_chunk_append_buffer (chunk,
128                                 ptr+1, len_nstr, free, len_str)) {
129                 free (len_str);
130                 return -1;
131         }
132         
133         return 0;
134 }
135 
136 int rh_chunk_append_buffer (rh_chunk_t *chunk,
137                 void *src, size_t nsrc,
138                 void (*cb)(void *), void *cb_arg)
139 {
140         struct rh_chunk_entry           *entry;
141         struct rh_chunk_entry_iovec     *iovec;
142 
143         entry = TAILQ_LAST(&chunk->list, rh_chunk_list);
144         
145         if (    NULL != entry &&
146                 entry->type == RH_CHUNK_ENTRY_IOVEC &&
147                 entry->data.iovec->used < RH_CHUNK_IOVEC_SZ)
148         {
149                 iovec = entry->data.iovec;
150         } else {
151                 entry = malloc (sizeof(*entry));
152                 if (NULL == entry)
153                         return -1;
154                 
155                 iovec = malloc (sizeof(*iovec));
156                 if (NULL == iovec) {
157                         free (entry);
158                         return -1;
159                 }
160                 
161                 entry->type = RH_CHUNK_ENTRY_IOVEC;
162                 entry->data.iovec = iovec;
163                 iovec->used = 0;
164                 iovec->offset = 0;
165                 iovec->size = 0;
166 
167                 TAILQ_INSERT_TAIL(&chunk->list, entry, list);
168         }
169 
170         iovec->vec[iovec->used].iov_base = src;
171         iovec->vec[iovec->used].iov_len  = nsrc;
172         
173         iovec->cb[iovec->used] = cb;
174         iovec->cb_arg[iovec->used] = cb_arg;
175 
176         ++iovec->used;
177 
178         iovec->size += nsrc;
179 
180         chunk->size_in += nsrc;
181         
182         return 0;
183 }
184 
185 static void rh_chunk_destroy_iovec (rh_chunk_t *chunk,
186                 struct rh_chunk_entry *entry)
187 {
188         struct rh_chunk_entry_iovec     *iovec;
189         unsigned short                  i;
190 
191         TAILQ_REMOVE(&chunk->list, entry, list);
192 
193         iovec = entry->data.iovec;
194 
195         for (i=iovec->offset; i<iovec->used; ++i) {
196                 if (    NULL == iovec->cb[i] ||
197                         NULL == iovec->cb_arg[i])
198                 {
199                         continue;
200                 }
201                 
202                 iovec->cb[i] (iovec->cb_arg[i]);
203                 iovec->cb[i] = NULL;
204 
205         }
206         iovec->used = 0;
207 
208         free (iovec);
209         free (entry);
210         
211 }
212 
213 static int rh_chunk_write_iovec (rh_chunk_t *chunk, int fd,
214                 struct rh_chunk_entry *entry)
215 {
216         struct rh_chunk_entry_iovec     *iovec;
217         ssize_t         wrote;
218         size_t          wrote_recalc;
219         unsigned short  i;
220 
221         iovec = entry->data.iovec;
222 
223         wrote = writev (fd, &iovec->vec[iovec->offset],
224                                 iovec->used - iovec->offset);
225 
226         if (wrote < 1)
227                 return -1;
228                         
229         iovec->size -= wrote;
230         chunk->size_out += wrote;
231         wrote_recalc = wrote;
232 
233         for (i=iovec->offset; i<iovec->used; ++i) {
234                 iovec->offset = i;
235                 
236                 if (wrote_recalc >= iovec->vec[i].iov_len) {    
237                         wrote_recalc -= iovec->vec[i].iov_len;
238                         
239                         if (    NULL != iovec->cb[i] &&
240                                 NULL != iovec->cb_arg[i]) 
241                         {
242                                 iovec->cb[i] (iovec->cb_arg[i]);
243                                 iovec->cb[i] = NULL;
244                         }
245                         
246                         continue;
247                 }
248 
249                 iovec->vec[i].iov_len -= wrote_recalc;
250                 iovec->vec[i].iov_base = ((char*)iovec->vec[i].iov_base) + wrote_recalc;
251                 return -1;
252         }
253         
254         TAILQ_REMOVE(&chunk->list, entry, list);
255         free (iovec);
256         free (entry);
257         
258         return 0;
259 }
260 
261 /*
262  *
263  *
264  *
265  *
266  *
267  *
268  * file
269  *
270  *
271  *
272  *
273  *
274  *
275  *
276  *
277  */
278 
279 int rh_chunk_append_file (rh_chunk_t *chunk,
280                 int fd, off_t offset, size_t size,
281                 void (*cb)(void *), void *cb_arg)
282 {
283         struct rh_chunk_entry           *entry;
284         struct rh_chunk_entry_file      *file;
285 
286         entry = malloc (sizeof(*entry));
287         if (NULL == entry)
288                 return -1;
289         
290         file = malloc (sizeof(*file));
291         if (NULL == file) {
292                 free (entry);
293                 return -1;
294         }
295                 
296         entry->type = RH_CHUNK_ENTRY_FILE;
297         entry->data.file = file;
298 
299         file->fd = fd;
300         file->offset = offset;
301         file->size = size;
302         file->cb = cb;
303         file->cb_arg = cb_arg;
304 
305         chunk->size_in += size;
306 
307         TAILQ_INSERT_TAIL(&chunk->list, entry, list);
308         
309         return 0;
310 }
311 
312 static int rh_chunk_write_file (rh_chunk_t *chunk, int fd,
313                 struct rh_chunk_entry *entry)
314 {
315         struct rh_chunk_entry_file      *file = entry->data.file;
316 
317         while (file->size) {
318                 ssize_t bytes;
319 #if 0
320                 printf ("sendfile: fd(%d) off(%llu) size(%zu)\n",
321                                 file->fd,
322                                 file->offset, file->size);
323 #endif
324                 
325                 bytes = sendfile (fd, file->fd, &(file->offset), file->size);
326 
327 #if 0
328                 printf ("  %zd bytes\n", bytes);
329 #endif
330                 
331                 if (bytes < 1)
332                         return -1;
333         
334                 chunk->size_out += bytes;
335 
336                 if (file->size > bytes) {
337                         file->size -= bytes;
338                 } else {
339                         file->size = 0;
340                 }
341         }
342         
343         TAILQ_REMOVE(&chunk->list, entry, list);
344 
345         if (file->cb && file->cb_arg)
346                 file->cb(file->cb_arg);
347         
348         free (file);
349         free (entry);
350         
351         return 0;
352 }


syntax highlighted by Code2HTML, v. 0.9.1