]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_subr/spillbuf.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_subr / spillbuf.c
1 /*
2  * spillbuf.c : an in-memory buffer that can spill to disk
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 #include <apr_file_io.h>
25
26 #include "svn_io.h"
27 #include "svn_pools.h"
28
29 #include "private/svn_subr_private.h"
30
31
32 struct memblock_t {
33   apr_size_t size;
34   char *data;
35
36   struct memblock_t *next;
37 };
38
39
40 struct svn_spillbuf_t {
41   /* Pool for allocating blocks and the spill file.  */
42   apr_pool_t *pool;
43
44   /* Size of in-memory blocks.  */
45   apr_size_t blocksize;
46
47   /* Maximum in-memory size; start spilling when we reach this size.  */
48   apr_size_t maxsize;
49
50   /* The amount of content in memory.  */
51   apr_size_t memory_size;
52
53   /* HEAD points to the first block of the linked list of buffers.
54      TAIL points to the last block, for quickly appending more blocks
55      to the overall list.  */
56   struct memblock_t *head;
57   struct memblock_t *tail;
58
59   /* Available blocks for storing pending data. These were allocated
60      previously, then the data consumed and returned to this list.  */
61   struct memblock_t *avail;
62
63   /* When a block is borrowed for reading, it is listed here.  */
64   struct memblock_t *out_for_reading;
65
66   /* Once MEMORY_SIZE exceeds SPILL_SIZE, then arriving content will be
67      appended to the (temporary) file indicated by SPILL.  */
68   apr_file_t *spill;
69
70   /* As we consume content from SPILL, this value indicates where we
71      will begin reading.  */
72   apr_off_t spill_start;
73
74   /* How much content remains in SPILL.  */
75   svn_filesize_t spill_size;
76 };
77
78
79 struct svn_spillbuf_reader_t {
80   /* Embed the spill-buffer within the reader.  */
81   struct svn_spillbuf_t buf;
82
83   /* When we read content from the underlying spillbuf, these fields store
84      the ptr/len pair. The ptr will be incremented as we "read" out of this
85      buffer since we don't have to retain the original pointer (it is
86      managed inside of the spillbuf).  */
87   const char *sb_ptr;
88   apr_size_t sb_len;
89
90   /* If a write comes in, then we may need to save content from our
91      borrowed buffer (since that buffer may be destroyed by our call into
92      the spillbuf code). Note that we retain the original pointer since
93      this buffer is allocated by the reader code and re-used. The SAVE_POS
94      field indicates the current position within this save buffer. The
95      SAVE_LEN field describes how much content is present.  */
96   char *save_ptr;
97   apr_size_t save_len;
98   apr_size_t save_pos;
99 };
100
101
102 svn_spillbuf_t *
103 svn_spillbuf__create(apr_size_t blocksize,
104                      apr_size_t maxsize,
105                      apr_pool_t *result_pool)
106 {
107   svn_spillbuf_t *buf = apr_pcalloc(result_pool, sizeof(*buf));
108
109   buf->pool = result_pool;
110   buf->blocksize = blocksize;
111   buf->maxsize = maxsize;
112   /* Note: changes here should also go into svn_spillbuf__reader_create() */
113
114   return buf;
115 }
116
117
118 svn_filesize_t
119 svn_spillbuf__get_size(const svn_spillbuf_t *buf)
120 {
121   return buf->memory_size + buf->spill_size;
122 }
123
124
125 /* Get a memblock from the spill-buffer. It will be the block that we
126    passed out for reading, come from the free list, or allocated.  */
127 static struct memblock_t *
128 get_buffer(svn_spillbuf_t *buf)
129 {
130   struct memblock_t *mem = buf->out_for_reading;
131
132   if (mem != NULL)
133     {
134       buf->out_for_reading = NULL;
135       return mem;
136     }
137
138   if (buf->avail == NULL)
139     {
140       mem = apr_palloc(buf->pool, sizeof(*mem));
141       mem->data = apr_palloc(buf->pool, buf->blocksize);
142       return mem;
143     }
144
145   mem = buf->avail;
146   buf->avail = mem->next;
147   return mem;
148 }
149
150
151 /* Return MEM to the list of available buffers in BUF.  */
152 static void
153 return_buffer(svn_spillbuf_t *buf,
154               struct memblock_t *mem)
155 {
156   mem->next = buf->avail;
157   buf->avail = mem;
158 }
159
160
161 svn_error_t *
162 svn_spillbuf__write(svn_spillbuf_t *buf,
163                     const char *data,
164                     apr_size_t len,
165                     apr_pool_t *scratch_pool)
166 {
167   struct memblock_t *mem;
168
169   /* We do not (yet) have a spill file, but the amount stored in memory
170      will grow too large. Create the file and place the pending data into
171      the temporary file.  */
172   if (buf->spill == NULL
173       && (buf->memory_size + len) > buf->maxsize)
174     {
175       SVN_ERR(svn_io_open_unique_file3(&buf->spill,
176                                        NULL /* temp_path */,
177                                        NULL /* dirpath */,
178                                        svn_io_file_del_on_close,
179                                        buf->pool, scratch_pool));
180     }
181
182   /* Once a spill file has been constructed, then we need to put all
183      arriving data into the file. We will no longer attempt to hold it
184      in memory.  */
185   if (buf->spill != NULL)
186     {
187       apr_off_t output_unused = 0;  /* ### stupid API  */
188
189       /* Seek to the end of the spill file. We don't know if a read has
190          occurred since our last write, and moved the file position.  */
191       SVN_ERR(svn_io_file_seek(buf->spill,
192                                APR_END, &output_unused,
193                                scratch_pool));
194
195       SVN_ERR(svn_io_file_write_full(buf->spill, data, len,
196                                      NULL, scratch_pool));
197       buf->spill_size += len;
198
199       return SVN_NO_ERROR;
200     }
201
202   while (len > 0)
203     {
204       apr_size_t amt;
205
206       if (buf->tail == NULL || buf->tail->size == buf->blocksize)
207         {
208           /* There is no existing memblock (that may have space), or the
209              tail memblock has no space, so we need a new memblock.  */
210           mem = get_buffer(buf);
211           mem->size = 0;
212           mem->next = NULL;
213         }
214       else
215         {
216           mem = buf->tail;
217         }
218
219       /* Compute how much to write into the memblock.  */
220       amt = buf->blocksize - mem->size;
221       if (amt > len)
222         amt = len;
223
224       /* Copy some data into this memblock.  */
225       memcpy(&mem->data[mem->size], data, amt);
226       mem->size += amt;
227       data += amt;
228       len -= amt;
229
230       /* We need to record how much is buffered in memory. Once we reach
231          buf->maxsize (or thereabouts, it doesn't have to be precise), then
232          we'll switch to putting the content into a file.  */
233       buf->memory_size += amt;
234
235       /* Start a list of buffers, or (if we're not writing into the tail)
236          append to the end of the linked list of buffers.  */
237       if (buf->tail == NULL)
238         {
239           buf->head = mem;
240           buf->tail = mem;
241         }
242       else if (mem != buf->tail)
243         {
244           buf->tail->next = mem;
245           buf->tail = mem;
246         }
247     }
248
249   return SVN_NO_ERROR;
250 }
251
252
253 /* Return a memblock of content, if any is available. *mem will be NULL if
254    no further content is available. The memblock should eventually be
255    passed to return_buffer() (or stored into buf->out_for_reading which
256    will grab that block at the next get_buffer() call). */
257 static svn_error_t *
258 read_data(struct memblock_t **mem,
259           svn_spillbuf_t *buf,
260           apr_pool_t *scratch_pool)
261 {
262   svn_error_t *err;
263
264   /* If we have some in-memory blocks, then return one.  */
265   if (buf->head != NULL)
266     {
267       *mem = buf->head;
268       if (buf->tail == *mem)
269         buf->head = buf->tail = NULL;
270       else
271         buf->head = (*mem)->next;
272
273       /* We're using less memory now. If we haven't hit the spill file,
274          then we may be able to keep using memory.  */
275       buf->memory_size -= (*mem)->size;
276
277       return SVN_NO_ERROR;
278     }
279
280   /* No file? Done.  */
281   if (buf->spill == NULL)
282     {
283       *mem = NULL;
284       return SVN_NO_ERROR;
285     }
286
287   /* Assume that the caller has seeked the spill file to the correct pos.  */
288
289   /* Get a buffer that we can read content into.  */
290   *mem = get_buffer(buf);
291   /* NOTE: mem's size/next are uninitialized.  */
292
293   if ((apr_uint64_t)buf->spill_size < (apr_uint64_t)buf->blocksize)
294     (*mem)->size = (apr_size_t)buf->spill_size;
295   else
296     (*mem)->size = buf->blocksize;  /* The size of (*mem)->data  */
297   (*mem)->next = NULL;
298
299   /* Read some data from the spill file into the memblock.  */
300   err = svn_io_file_read(buf->spill, (*mem)->data, &(*mem)->size,
301                          scratch_pool);
302   if (err)
303     {
304       return_buffer(buf, *mem);
305       return svn_error_trace(err);
306     }
307
308   /* Mark the data that we consumed from the spill file.  */
309   buf->spill_start += (*mem)->size;
310
311   /* Did we consume all the data from the spill file?  */
312   if ((buf->spill_size -= (*mem)->size) == 0)
313     {
314       /* Close and reset our spill file information.  */
315       SVN_ERR(svn_io_file_close(buf->spill, scratch_pool));
316       buf->spill = NULL;
317       buf->spill_start = 0;
318     }
319
320   /* *mem has been initialized. Done.  */
321   return SVN_NO_ERROR;
322 }
323
324
325 /* If the next read would consume data from the file, then seek to the
326    correct position.  */
327 static svn_error_t *
328 maybe_seek(svn_boolean_t *seeked,
329            const svn_spillbuf_t *buf,
330            apr_pool_t *scratch_pool)
331 {
332   if (buf->head == NULL && buf->spill != NULL)
333     {
334       apr_off_t output_unused;
335
336       /* Seek to where we left off reading.  */
337       output_unused = buf->spill_start;  /* ### stupid API  */
338       SVN_ERR(svn_io_file_seek(buf->spill,
339                                APR_SET, &output_unused,
340                                scratch_pool));
341       if (seeked != NULL)
342         *seeked = TRUE;
343     }
344   else if (seeked != NULL)
345     {
346       *seeked = FALSE;
347     }
348
349   return SVN_NO_ERROR;
350 }
351
352
353 svn_error_t *
354 svn_spillbuf__read(const char **data,
355                    apr_size_t *len,
356                    svn_spillbuf_t *buf,
357                    apr_pool_t *scratch_pool)
358 {
359   struct memblock_t *mem;
360
361   /* Possibly seek... */
362   SVN_ERR(maybe_seek(NULL, buf, scratch_pool));
363
364   SVN_ERR(read_data(&mem, buf, scratch_pool));
365   if (mem == NULL)
366     {
367       *data = NULL;
368       *len = 0;
369     }
370   else
371     {
372       *data = mem->data;
373       *len = mem->size;
374
375       /* If a block was out for reading, then return it.  */
376       if (buf->out_for_reading != NULL)
377         return_buffer(buf, buf->out_for_reading);
378
379       /* Remember that we've passed this block out for reading.  */
380       buf->out_for_reading = mem;
381     }
382
383   return SVN_NO_ERROR;
384 }
385
386
387 svn_error_t *
388 svn_spillbuf__process(svn_boolean_t *exhausted,
389                       svn_spillbuf_t *buf,
390                       svn_spillbuf_read_t read_func,
391                       void *read_baton,
392                       apr_pool_t *scratch_pool)
393 {
394   svn_boolean_t has_seeked = FALSE;
395   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
396
397   *exhausted = FALSE;
398
399   while (TRUE)
400     {
401       struct memblock_t *mem;
402       svn_error_t *err;
403       svn_boolean_t stop;
404
405       svn_pool_clear(iterpool);
406
407       /* If this call to read_data() will read from the spill file, and we
408          have not seek'd the file... then do it now.  */
409       if (!has_seeked)
410         SVN_ERR(maybe_seek(&has_seeked, buf, iterpool));
411
412       /* Get some content to pass to the read callback.  */
413       SVN_ERR(read_data(&mem, buf, iterpool));
414       if (mem == NULL)
415         {
416           *exhausted = TRUE;
417           break;
418         }
419
420       err = read_func(&stop, read_baton, mem->data, mem->size, iterpool);
421
422       return_buffer(buf, mem);
423
424       if (err)
425         return svn_error_trace(err);
426
427       /* If the callbacks told us to stop, then we're done for now.  */
428       if (stop)
429         break;
430     }
431
432   svn_pool_destroy(iterpool);
433   return SVN_NO_ERROR;
434 }
435
436
437 svn_spillbuf_reader_t *
438 svn_spillbuf__reader_create(apr_size_t blocksize,
439                             apr_size_t maxsize,
440                             apr_pool_t *result_pool)
441 {
442   svn_spillbuf_reader_t *sbr = apr_pcalloc(result_pool, sizeof(*sbr));
443
444   /* See svn_spillbuf__create()  */
445   sbr->buf.pool = result_pool;
446   sbr->buf.blocksize = blocksize;
447   sbr->buf.maxsize = maxsize;
448
449   return sbr;
450 }
451
452
453 svn_error_t *
454 svn_spillbuf__reader_read(apr_size_t *amt,
455                           svn_spillbuf_reader_t *reader,
456                           char *data,
457                           apr_size_t len,
458                           apr_pool_t *scratch_pool)
459 {
460   if (len == 0)
461     return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, NULL);
462
463   *amt = 0;
464
465   while (len > 0)
466     {
467       apr_size_t copy_amt;
468
469       if (reader->save_len > 0)
470         {
471           /* We have some saved content, so use this first.  */
472
473           if (len < reader->save_len)
474             copy_amt = len;
475           else
476             copy_amt = reader->save_len;
477
478           memcpy(data, reader->save_ptr + reader->save_pos, copy_amt);
479           reader->save_pos += copy_amt;
480           reader->save_len -= copy_amt;
481         }
482       else
483         {
484           /* No saved content. We should now copy from spillbuf-provided
485              buffers of content.  */
486
487           /* We may need more content from the spillbuf.  */
488           if (reader->sb_len == 0)
489             {
490               SVN_ERR(svn_spillbuf__read(&reader->sb_ptr, &reader->sb_len,
491                                          &reader->buf,
492                                          scratch_pool));
493
494               /* We've run out of content, so return with whatever has
495                  been copied into DATA and stored into AMT.  */
496               if (reader->sb_ptr == NULL)
497                 {
498                   /* For safety, read() may not have set SB_LEN. We use it
499                      as an indicator, so it needs to be cleared.  */
500                   reader->sb_len = 0;
501                   return SVN_NO_ERROR;
502                 }
503             }
504
505           if (len < reader->sb_len)
506             copy_amt = len;
507           else
508             copy_amt = reader->sb_len;
509
510           memcpy(data, reader->sb_ptr, copy_amt);
511           reader->sb_ptr += copy_amt;
512           reader->sb_len -= copy_amt;
513         }
514
515       data += copy_amt;
516       len -= copy_amt;
517       (*amt) += copy_amt;
518     }
519
520   return SVN_NO_ERROR;
521 }
522
523
524 svn_error_t *
525 svn_spillbuf__reader_getc(char *c,
526                           svn_spillbuf_reader_t *reader,
527                           apr_pool_t *scratch_pool)
528 {
529   apr_size_t amt;
530
531   SVN_ERR(svn_spillbuf__reader_read(&amt, reader, c, 1, scratch_pool));
532   if (amt == 0)
533     return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, NULL);
534
535   return SVN_NO_ERROR;
536 }
537
538
539 svn_error_t *
540 svn_spillbuf__reader_write(svn_spillbuf_reader_t *reader,
541                            const char *data,
542                            apr_size_t len,
543                            apr_pool_t *scratch_pool)
544 {
545   /* If we have a buffer of content from the spillbuf, then we need to
546      move that content to a safe place.  */
547   if (reader->sb_len > 0)
548     {
549       if (reader->save_ptr == NULL)
550         reader->save_ptr = apr_palloc(reader->buf.pool, reader->buf.blocksize);
551
552       memcpy(reader->save_ptr, reader->sb_ptr, reader->sb_len);
553       reader->save_len = reader->sb_len;
554       reader->save_pos = 0;
555
556       /* No more content in the spillbuf-borrowed buffer.  */
557       reader->sb_len = 0;
558     }
559
560   return svn_error_trace(svn_spillbuf__write(&reader->buf, data, len,
561                                              scratch_pool));
562 }
563
564
565 struct spillbuf_baton
566 {
567   svn_spillbuf_reader_t *reader;
568   apr_pool_t *scratch_pool;
569 };
570
571
572 static svn_error_t *
573 read_handler_spillbuf(void *baton, char *buffer, apr_size_t *len)
574 {
575   struct spillbuf_baton *sb = baton;
576
577   SVN_ERR(svn_spillbuf__reader_read(len, sb->reader, buffer, *len,
578                                     sb->scratch_pool));
579
580   svn_pool_clear(sb->scratch_pool);
581   return SVN_NO_ERROR;
582 }
583
584
585 static svn_error_t *
586 write_handler_spillbuf(void *baton, const char *data, apr_size_t *len)
587 {
588   struct spillbuf_baton *sb = baton;
589
590   SVN_ERR(svn_spillbuf__reader_write(sb->reader, data, *len,
591                                      sb->scratch_pool));
592
593   svn_pool_clear(sb->scratch_pool);
594   return SVN_NO_ERROR;
595 }
596
597
598 svn_stream_t *
599 svn_stream__from_spillbuf(apr_size_t blocksize,
600                           apr_size_t maxsize,
601                           apr_pool_t *result_pool)
602 {
603   svn_stream_t *stream;
604   struct spillbuf_baton *sb = apr_palloc(result_pool, sizeof(*sb));
605
606   sb->reader = svn_spillbuf__reader_create(blocksize, maxsize, result_pool);
607   sb->scratch_pool = svn_pool_create(result_pool);
608
609   stream = svn_stream_create(sb, result_pool);
610
611   svn_stream_set_read(stream, read_handler_spillbuf);
612   svn_stream_set_write(stream, write_handler_spillbuf);
613
614   return stream;
615 }