]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_subr/spillbuf.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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   /* When false, do not delete the spill file when it is closed. */
78   svn_boolean_t delete_on_close;
79
80   /* When true, and the amount of data written to the spillbuf is
81      larger than MAXSIZE, all spillbuf contents will be written to the
82      spill file. */
83   svn_boolean_t spill_all_contents;
84
85   /* The directory in which the spill file is created. */
86   const char *dirpath;
87
88   /* The name of the temporary spill file. */
89   const char *filename;
90 };
91
92
93 struct svn_spillbuf_reader_t {
94   /* Embed the spill-buffer within the reader.  */
95   struct svn_spillbuf_t *buf;
96
97   /* When we read content from the underlying spillbuf, these fields store
98      the ptr/len pair. The ptr will be incremented as we "read" out of this
99      buffer since we don't have to retain the original pointer (it is
100      managed inside of the spillbuf).  */
101   const char *sb_ptr;
102   apr_size_t sb_len;
103
104   /* If a write comes in, then we may need to save content from our
105      borrowed buffer (since that buffer may be destroyed by our call into
106      the spillbuf code). Note that we retain the original pointer since
107      this buffer is allocated by the reader code and re-used. The SAVE_POS
108      field indicates the current position within this save buffer. The
109      SAVE_LEN field describes how much content is present.  */
110   char *save_ptr;
111   apr_size_t save_len;
112   apr_size_t save_pos;
113 };
114
115
116 /* Extended spillbuf initialization. */
117 static void
118 init_spillbuf_extended(svn_spillbuf_t *buf,
119                        apr_size_t blocksize,
120                        apr_size_t maxsize,
121                        svn_boolean_t delete_on_close,
122                        svn_boolean_t spill_all_contents,
123                        const char *dirpath,
124                        apr_pool_t *result_pool)
125 {
126   buf->pool = result_pool;
127   buf->blocksize = blocksize;
128   buf->maxsize = maxsize;
129   buf->delete_on_close = delete_on_close;
130   buf->spill_all_contents = spill_all_contents;
131   buf->dirpath = dirpath;
132 }
133
134 /* Common constructor for initializing spillbufs.
135    Used by svn_spillbuf__create, svn_spilbuff__reader_create. */
136 static void
137 init_spillbuf(svn_spillbuf_t *buf,
138               apr_size_t blocksize,
139               apr_size_t maxsize,
140               apr_pool_t *result_pool)
141 {
142   init_spillbuf_extended(buf, blocksize, maxsize,
143                          TRUE, FALSE, NULL,
144                          result_pool);
145 }
146
147 svn_spillbuf_t *
148 svn_spillbuf__create(apr_size_t blocksize,
149                      apr_size_t maxsize,
150                      apr_pool_t *result_pool)
151 {
152   svn_spillbuf_t *buf = apr_pcalloc(result_pool, sizeof(*buf));
153   init_spillbuf(buf, blocksize, maxsize, result_pool);
154   return buf;
155 }
156
157
158 svn_spillbuf_t *
159 svn_spillbuf__create_extended(apr_size_t blocksize,
160                               apr_size_t maxsize,
161                               svn_boolean_t delete_on_close,
162                               svn_boolean_t spill_all_contents,
163                               const char *dirpath,
164                               apr_pool_t *result_pool)
165 {
166   svn_spillbuf_t *buf = apr_pcalloc(result_pool, sizeof(*buf));
167   init_spillbuf_extended(buf, blocksize, maxsize,
168                          delete_on_close, spill_all_contents, dirpath,
169                          result_pool);
170   return buf;
171 }
172
173 svn_filesize_t
174 svn_spillbuf__get_size(const svn_spillbuf_t *buf)
175 {
176   return buf->memory_size + buf->spill_size;
177 }
178
179 svn_filesize_t
180 svn_spillbuf__get_memory_size(const svn_spillbuf_t *buf)
181 {
182   return buf->memory_size;
183 }
184
185 const char *
186 svn_spillbuf__get_filename(const svn_spillbuf_t *buf)
187 {
188   return buf->filename;
189 }
190
191 apr_file_t *
192 svn_spillbuf__get_file(const svn_spillbuf_t *buf)
193 {
194   return buf->spill;
195 }
196
197 /* Get a memblock from the spill-buffer. It will be the block that we
198    passed out for reading, come from the free list, or allocated.  */
199 static struct memblock_t *
200 get_buffer(svn_spillbuf_t *buf)
201 {
202   struct memblock_t *mem = buf->out_for_reading;
203
204   if (mem != NULL)
205     {
206       buf->out_for_reading = NULL;
207       return mem;
208     }
209
210   if (buf->avail == NULL)
211     {
212       mem = apr_palloc(buf->pool, sizeof(*mem));
213       mem->data = apr_palloc(buf->pool, buf->blocksize);
214       return mem;
215     }
216
217   mem = buf->avail;
218   buf->avail = mem->next;
219   return mem;
220 }
221
222
223 /* Return MEM to the list of available buffers in BUF.  */
224 static void
225 return_buffer(svn_spillbuf_t *buf,
226               struct memblock_t *mem)
227 {
228   mem->next = buf->avail;
229   buf->avail = mem;
230 }
231
232
233 svn_error_t *
234 svn_spillbuf__write(svn_spillbuf_t *buf,
235                     const char *data,
236                     apr_size_t len,
237                     apr_pool_t *scratch_pool)
238 {
239   struct memblock_t *mem;
240
241   /* We do not (yet) have a spill file, but the amount stored in memory
242      will grow too large. Create the file and place the pending data into
243      the temporary file.  */
244   if (buf->spill == NULL
245       && (buf->memory_size + len) > buf->maxsize)
246     {
247       SVN_ERR(svn_io_open_unique_file3(&buf->spill,
248                                        &buf->filename,
249                                        buf->dirpath,
250                                        (buf->delete_on_close
251                                         ? svn_io_file_del_on_close
252                                         : svn_io_file_del_none),
253                                        buf->pool, scratch_pool));
254
255       /* Optionally write the memory contents into the file. */
256       if (buf->spill_all_contents)
257         {
258           mem = buf->head;
259           while (mem != NULL)
260             {
261               SVN_ERR(svn_io_file_write_full(buf->spill, mem->data, mem->size,
262                                              NULL, scratch_pool));
263               mem = mem->next;
264             }
265
266           /* Adjust the start offset for reading from the spill file.
267
268              ### FIXME: Instead, we should simply discard the memory
269              buffers; but currently some tests expect to read data in
270              the same chunk sizes as were written, so we'll leave this
271              change for later.*/
272           buf->spill_start = buf->memory_size;
273         }
274     }
275
276   /* Once a spill file has been constructed, then we need to put all
277      arriving data into the file. We will no longer attempt to hold it
278      in memory.  */
279   if (buf->spill != NULL)
280     {
281       apr_off_t output_unused = 0;  /* ### stupid API  */
282
283       /* Seek to the end of the spill file. We don't know if a read has
284          occurred since our last write, and moved the file position.  */
285       SVN_ERR(svn_io_file_seek(buf->spill,
286                                APR_END, &output_unused,
287                                scratch_pool));
288
289       SVN_ERR(svn_io_file_write_full(buf->spill, data, len,
290                                      NULL, scratch_pool));
291       buf->spill_size += len;
292
293       return SVN_NO_ERROR;
294     }
295
296   while (len > 0)
297     {
298       apr_size_t amt;
299
300       if (buf->tail == NULL || buf->tail->size == buf->blocksize)
301         {
302           /* There is no existing memblock (that may have space), or the
303              tail memblock has no space, so we need a new memblock.  */
304           mem = get_buffer(buf);
305           mem->size = 0;
306           mem->next = NULL;
307         }
308       else
309         {
310           mem = buf->tail;
311         }
312
313       /* Compute how much to write into the memblock.  */
314       amt = buf->blocksize - mem->size;
315       if (amt > len)
316         amt = len;
317
318       /* Copy some data into this memblock.  */
319       memcpy(&mem->data[mem->size], data, amt);
320       mem->size += amt;
321       data += amt;
322       len -= amt;
323
324       /* We need to record how much is buffered in memory. Once we reach
325          buf->maxsize (or thereabouts, it doesn't have to be precise), then
326          we'll switch to putting the content into a file.  */
327       buf->memory_size += amt;
328
329       /* Start a list of buffers, or (if we're not writing into the tail)
330          append to the end of the linked list of buffers.  */
331       if (buf->tail == NULL)
332         {
333           buf->head = mem;
334           buf->tail = mem;
335         }
336       else if (mem != buf->tail)
337         {
338           buf->tail->next = mem;
339           buf->tail = mem;
340         }
341     }
342
343   return SVN_NO_ERROR;
344 }
345
346
347 /* Return a memblock of content, if any is available. *mem will be NULL if
348    no further content is available. The memblock should eventually be
349    passed to return_buffer() (or stored into buf->out_for_reading which
350    will grab that block at the next get_buffer() call). */
351 static svn_error_t *
352 read_data(struct memblock_t **mem,
353           svn_spillbuf_t *buf,
354           apr_pool_t *scratch_pool)
355 {
356   svn_error_t *err;
357
358   /* If we have some in-memory blocks, then return one.  */
359   if (buf->head != NULL)
360     {
361       *mem = buf->head;
362       if (buf->tail == *mem)
363         buf->head = buf->tail = NULL;
364       else
365         buf->head = (*mem)->next;
366
367       /* We're using less memory now. If we haven't hit the spill file,
368          then we may be able to keep using memory.  */
369       buf->memory_size -= (*mem)->size;
370
371       return SVN_NO_ERROR;
372     }
373
374   /* No file? Done.  */
375   if (buf->spill == NULL)
376     {
377       *mem = NULL;
378       return SVN_NO_ERROR;
379     }
380
381   /* Assume that the caller has seeked the spill file to the correct pos.  */
382
383   /* Get a buffer that we can read content into.  */
384   *mem = get_buffer(buf);
385   /* NOTE: mem's size/next are uninitialized.  */
386
387   if ((apr_uint64_t)buf->spill_size < (apr_uint64_t)buf->blocksize)
388     (*mem)->size = (apr_size_t)buf->spill_size;
389   else
390     (*mem)->size = buf->blocksize;  /* The size of (*mem)->data  */
391   (*mem)->next = NULL;
392
393   /* Read some data from the spill file into the memblock.  */
394   err = svn_io_file_read(buf->spill, (*mem)->data, &(*mem)->size,
395                          scratch_pool);
396   if (err)
397     {
398       return_buffer(buf, *mem);
399       return svn_error_trace(err);
400     }
401
402   /* Mark the data that we consumed from the spill file.  */
403   buf->spill_start += (*mem)->size;
404
405   /* Did we consume all the data from the spill file?  */
406   if ((buf->spill_size -= (*mem)->size) == 0)
407     {
408       /* Close and reset our spill file information.  */
409       SVN_ERR(svn_io_file_close(buf->spill, scratch_pool));
410       buf->spill = NULL;
411       buf->spill_start = 0;
412     }
413
414   /* *mem has been initialized. Done.  */
415   return SVN_NO_ERROR;
416 }
417
418
419 /* If the next read would consume data from the file, then seek to the
420    correct position.  */
421 static svn_error_t *
422 maybe_seek(svn_boolean_t *seeked,
423            const svn_spillbuf_t *buf,
424            apr_pool_t *scratch_pool)
425 {
426   if (buf->head == NULL && buf->spill != NULL)
427     {
428       apr_off_t output_unused;
429
430       /* Seek to where we left off reading.  */
431       output_unused = buf->spill_start;  /* ### stupid API  */
432       SVN_ERR(svn_io_file_seek(buf->spill,
433                                APR_SET, &output_unused,
434                                scratch_pool));
435       if (seeked != NULL)
436         *seeked = TRUE;
437     }
438   else if (seeked != NULL)
439     {
440       *seeked = FALSE;
441     }
442
443   return SVN_NO_ERROR;
444 }
445
446
447 svn_error_t *
448 svn_spillbuf__read(const char **data,
449                    apr_size_t *len,
450                    svn_spillbuf_t *buf,
451                    apr_pool_t *scratch_pool)
452 {
453   struct memblock_t *mem;
454
455   /* Possibly seek... */
456   SVN_ERR(maybe_seek(NULL, buf, scratch_pool));
457
458   SVN_ERR(read_data(&mem, buf, scratch_pool));
459   if (mem == NULL)
460     {
461       *data = NULL;
462       *len = 0;
463     }
464   else
465     {
466       *data = mem->data;
467       *len = mem->size;
468
469       /* If a block was out for reading, then return it.  */
470       if (buf->out_for_reading != NULL)
471         return_buffer(buf, buf->out_for_reading);
472
473       /* Remember that we've passed this block out for reading.  */
474       buf->out_for_reading = mem;
475     }
476
477   return SVN_NO_ERROR;
478 }
479
480
481 svn_error_t *
482 svn_spillbuf__process(svn_boolean_t *exhausted,
483                       svn_spillbuf_t *buf,
484                       svn_spillbuf_read_t read_func,
485                       void *read_baton,
486                       apr_pool_t *scratch_pool)
487 {
488   svn_boolean_t has_seeked = FALSE;
489   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
490
491   *exhausted = FALSE;
492
493   while (TRUE)
494     {
495       struct memblock_t *mem;
496       svn_error_t *err;
497       svn_boolean_t stop;
498
499       svn_pool_clear(iterpool);
500
501       /* If this call to read_data() will read from the spill file, and we
502          have not seek'd the file... then do it now.  */
503       if (!has_seeked)
504         SVN_ERR(maybe_seek(&has_seeked, buf, iterpool));
505
506       /* Get some content to pass to the read callback.  */
507       SVN_ERR(read_data(&mem, buf, iterpool));
508       if (mem == NULL)
509         {
510           *exhausted = TRUE;
511           break;
512         }
513
514       err = read_func(&stop, read_baton, mem->data, mem->size, iterpool);
515
516       return_buffer(buf, mem);
517
518       if (err)
519         return svn_error_trace(err);
520
521       /* If the callbacks told us to stop, then we're done for now.  */
522       if (stop)
523         break;
524     }
525
526   svn_pool_destroy(iterpool);
527   return SVN_NO_ERROR;
528 }
529
530
531 svn_spillbuf_reader_t *
532 svn_spillbuf__reader_create(apr_size_t blocksize,
533                             apr_size_t maxsize,
534                             apr_pool_t *result_pool)
535 {
536   svn_spillbuf_reader_t *sbr = apr_pcalloc(result_pool, sizeof(*sbr));
537   sbr->buf = svn_spillbuf__create(blocksize, maxsize, result_pool);
538   return sbr;
539 }
540
541 svn_error_t *
542 svn_spillbuf__reader_read(apr_size_t *amt,
543                           svn_spillbuf_reader_t *reader,
544                           char *data,
545                           apr_size_t len,
546                           apr_pool_t *scratch_pool)
547 {
548   if (len == 0)
549     return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, NULL);
550
551   *amt = 0;
552
553   while (len > 0)
554     {
555       apr_size_t copy_amt;
556
557       if (reader->save_len > 0)
558         {
559           /* We have some saved content, so use this first.  */
560
561           if (len < reader->save_len)
562             copy_amt = len;
563           else
564             copy_amt = reader->save_len;
565
566           memcpy(data, reader->save_ptr + reader->save_pos, copy_amt);
567           reader->save_pos += copy_amt;
568           reader->save_len -= copy_amt;
569         }
570       else
571         {
572           /* No saved content. We should now copy from spillbuf-provided
573              buffers of content.  */
574
575           /* We may need more content from the spillbuf.  */
576           if (reader->sb_len == 0)
577             {
578               SVN_ERR(svn_spillbuf__read(&reader->sb_ptr, &reader->sb_len,
579                                          reader->buf,
580                                          scratch_pool));
581
582               /* We've run out of content, so return with whatever has
583                  been copied into DATA and stored into AMT.  */
584               if (reader->sb_ptr == NULL)
585                 {
586                   /* For safety, read() may not have set SB_LEN. We use it
587                      as an indicator, so it needs to be cleared.  */
588                   reader->sb_len = 0;
589                   return SVN_NO_ERROR;
590                 }
591             }
592
593           if (len < reader->sb_len)
594             copy_amt = len;
595           else
596             copy_amt = reader->sb_len;
597
598           memcpy(data, reader->sb_ptr, copy_amt);
599           reader->sb_ptr += copy_amt;
600           reader->sb_len -= copy_amt;
601         }
602
603       data += copy_amt;
604       len -= copy_amt;
605       (*amt) += copy_amt;
606     }
607
608   return SVN_NO_ERROR;
609 }
610
611
612 svn_error_t *
613 svn_spillbuf__reader_getc(char *c,
614                           svn_spillbuf_reader_t *reader,
615                           apr_pool_t *scratch_pool)
616 {
617   apr_size_t amt;
618
619   SVN_ERR(svn_spillbuf__reader_read(&amt, reader, c, 1, scratch_pool));
620   if (amt == 0)
621     return svn_error_create(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL, NULL);
622
623   return SVN_NO_ERROR;
624 }
625
626
627 svn_error_t *
628 svn_spillbuf__reader_write(svn_spillbuf_reader_t *reader,
629                            const char *data,
630                            apr_size_t len,
631                            apr_pool_t *scratch_pool)
632 {
633   /* If we have a buffer of content from the spillbuf, then we need to
634      move that content to a safe place.  */
635   if (reader->sb_len > 0)
636     {
637       if (reader->save_ptr == NULL)
638         reader->save_ptr = apr_palloc(reader->buf->pool,
639                                       reader->buf->blocksize);
640
641       memcpy(reader->save_ptr, reader->sb_ptr, reader->sb_len);
642       reader->save_len = reader->sb_len;
643       reader->save_pos = 0;
644
645       /* No more content in the spillbuf-borrowed buffer.  */
646       reader->sb_len = 0;
647     }
648
649   return svn_error_trace(svn_spillbuf__write(reader->buf, data, len,
650                                              scratch_pool));
651 }
652
653
654 struct spillbuf_baton
655 {
656   svn_spillbuf_reader_t *reader;
657   apr_pool_t *scratch_pool;
658 };
659
660
661 static svn_error_t *
662 read_handler_spillbuf(void *baton, char *buffer, apr_size_t *len)
663 {
664   struct spillbuf_baton *sb = baton;
665
666   SVN_ERR(svn_spillbuf__reader_read(len, sb->reader, buffer, *len,
667                                     sb->scratch_pool));
668
669   svn_pool_clear(sb->scratch_pool);
670   return SVN_NO_ERROR;
671 }
672
673
674 static svn_error_t *
675 write_handler_spillbuf(void *baton, const char *data, apr_size_t *len)
676 {
677   struct spillbuf_baton *sb = baton;
678
679   SVN_ERR(svn_spillbuf__reader_write(sb->reader, data, *len,
680                                      sb->scratch_pool));
681
682   svn_pool_clear(sb->scratch_pool);
683   return SVN_NO_ERROR;
684 }
685
686
687 svn_stream_t *
688 svn_stream__from_spillbuf(svn_spillbuf_t *buf,
689                           apr_pool_t *result_pool)
690 {
691   svn_stream_t *stream;
692   struct spillbuf_baton *sb = apr_palloc(result_pool, sizeof(*sb));
693
694   sb->reader = apr_pcalloc(result_pool, sizeof(*sb->reader));
695   sb->reader->buf = buf;
696   sb->scratch_pool = svn_pool_create(result_pool);
697
698   stream = svn_stream_create(sb, result_pool);
699
700   svn_stream_set_read2(stream, NULL /* only full read support */,
701                        read_handler_spillbuf);
702   svn_stream_set_write(stream, write_handler_spillbuf);
703
704   return stream;
705 }