Win32: fix an incorrect error status being propagated to the caller in case
[svn/apache.git] / subversion / libsvn_subr / spillbuf.c
blobc28780bab16d5ab107767c336113581606cd1084
1 /*
2 * spillbuf.c : an in-memory buffer that can spill to disk
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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 * ====================================================================
24 #include <apr_file_io.h>
26 #include "svn_io.h"
27 #include "svn_pools.h"
29 #include "private/svn_subr_private.h"
32 struct memblock_t {
33 apr_size_t size;
34 char *data;
36 struct memblock_t *next;
40 struct svn_spillbuf_t {
41 /* Pool for allocating blocks and the spill file. */
42 apr_pool_t *pool;
44 /* Size of in-memory blocks. */
45 apr_size_t blocksize;
47 /* Maximum in-memory size; start spilling when we reach this size. */
48 apr_size_t maxsize;
50 /* The amount of content in memory. */
51 apr_size_t memory_size;
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;
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;
63 /* When a block is borrowed for reading, it is listed here. */
64 struct memblock_t *out_for_reading;
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;
70 /* As we consume content from SPILL, this value indicates where we
71 will begin reading. */
72 apr_off_t spill_start;
74 /* How much content remains in SPILL. */
75 svn_filesize_t spill_size;
77 /* When false, do not delete the spill file when it is closed. */
78 svn_boolean_t delete_on_close;
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;
85 /* The directory in which the spill file is created. */
86 const char *dirpath;
88 /* The name of the temporary spill file. */
89 const char *filename;
93 struct svn_spillbuf_reader_t {
94 /* Embed the spill-buffer within the reader. */
95 struct svn_spillbuf_t *buf;
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;
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;
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)
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;
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)
142 init_spillbuf_extended(buf, blocksize, maxsize,
143 TRUE, FALSE, NULL,
144 result_pool);
147 svn_spillbuf_t *
148 svn_spillbuf__create(apr_size_t blocksize,
149 apr_size_t maxsize,
150 apr_pool_t *result_pool)
152 svn_spillbuf_t *buf = apr_pcalloc(result_pool, sizeof(*buf));
153 init_spillbuf(buf, blocksize, maxsize, result_pool);
154 return buf;
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)
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;
173 svn_filesize_t
174 svn_spillbuf__get_size(const svn_spillbuf_t *buf)
176 return buf->memory_size + buf->spill_size;
179 svn_filesize_t
180 svn_spillbuf__get_memory_size(const svn_spillbuf_t *buf)
182 return buf->memory_size;
185 const char *
186 svn_spillbuf__get_filename(const svn_spillbuf_t *buf)
188 return buf->filename;
191 apr_file_t *
192 svn_spillbuf__get_file(const svn_spillbuf_t *buf)
194 return buf->spill;
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)
202 struct memblock_t *mem = buf->out_for_reading;
204 if (mem != NULL)
206 buf->out_for_reading = NULL;
207 return mem;
210 if (buf->avail == NULL)
212 mem = apr_palloc(buf->pool, sizeof(*mem));
213 mem->data = apr_palloc(buf->pool, buf->blocksize);
214 return mem;
217 mem = buf->avail;
218 buf->avail = mem->next;
219 return mem;
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)
228 mem->next = buf->avail;
229 buf->avail = mem;
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)
239 struct memblock_t *mem;
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->maxsize - buf->memory_size) < len))
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));
255 /* Optionally write the memory contents into the file. */
256 if (buf->spill_all_contents)
258 mem = buf->head;
259 while (mem != NULL)
261 SVN_ERR(svn_io_file_write_full(buf->spill, mem->data, mem->size,
262 NULL, scratch_pool));
263 mem = mem->next;
266 /* Adjust the start offset for reading from the spill file.
268 This way, the first `buf->memory_size` bytes of data will
269 be read from the existing in-memory buffers, which makes
270 more sense than discarding the buffers and re-reading
271 data from the file. */
272 buf->spill_start = buf->memory_size;
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)
281 apr_off_t output_unused = 0; /* ### stupid API */
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));
289 SVN_ERR(svn_io_file_write_full(buf->spill, data, len,
290 NULL, scratch_pool));
291 buf->spill_size += len;
293 return SVN_NO_ERROR;
296 while (len > 0)
298 apr_size_t amt;
300 if (buf->tail == NULL || buf->tail->size == buf->blocksize)
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;
308 else
310 mem = buf->tail;
313 /* Compute how much to write into the memblock. */
314 amt = buf->blocksize - mem->size;
315 if (amt > len)
316 amt = len;
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;
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;
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)
333 buf->head = mem;
334 buf->tail = mem;
336 else if (mem != buf->tail)
338 buf->tail->next = mem;
339 buf->tail = mem;
343 return SVN_NO_ERROR;
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)
356 svn_error_t *err;
358 /* If we have some in-memory blocks, then return one. */
359 if (buf->head != NULL)
361 *mem = buf->head;
362 if (buf->tail == *mem)
363 buf->head = buf->tail = NULL;
364 else
365 buf->head = (*mem)->next;
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;
371 return SVN_NO_ERROR;
374 /* No file? Done. */
375 if (buf->spill == NULL)
377 *mem = NULL;
378 return SVN_NO_ERROR;
381 /* Assume that the caller has seeked the spill file to the correct pos. */
383 /* Get a buffer that we can read content into. */
384 *mem = get_buffer(buf);
385 /* NOTE: mem's size/next are uninitialized. */
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;
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)
398 return_buffer(buf, *mem);
399 return svn_error_trace(err);
402 /* Mark the data that we consumed from the spill file. */
403 buf->spill_start += (*mem)->size;
405 /* Did we consume all the data from the spill file? */
406 if ((buf->spill_size -= (*mem)->size) == 0)
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;
414 /* *mem has been initialized. Done. */
415 return SVN_NO_ERROR;
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)
426 if (buf->head == NULL && buf->spill != NULL)
428 apr_off_t output_unused;
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;
438 else if (seeked != NULL)
440 *seeked = FALSE;
443 return SVN_NO_ERROR;
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)
453 struct memblock_t *mem;
455 /* Possibly seek... */
456 SVN_ERR(maybe_seek(NULL, buf, scratch_pool));
458 SVN_ERR(read_data(&mem, buf, scratch_pool));
459 if (mem == NULL)
461 *data = NULL;
462 *len = 0;
464 else
466 *data = mem->data;
467 *len = mem->size;
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);
473 /* Remember that we've passed this block out for reading. */
474 buf->out_for_reading = mem;
477 return SVN_NO_ERROR;
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)
488 svn_boolean_t has_seeked = FALSE;
489 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
491 *exhausted = FALSE;
493 while (TRUE)
495 struct memblock_t *mem;
496 svn_error_t *err;
497 svn_boolean_t stop;
499 svn_pool_clear(iterpool);
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));
506 /* Get some content to pass to the read callback. */
507 SVN_ERR(read_data(&mem, buf, iterpool));
508 if (mem == NULL)
510 *exhausted = TRUE;
511 break;
514 err = read_func(&stop, read_baton, mem->data, mem->size, iterpool);
516 return_buffer(buf, mem);
518 if (err)
519 return svn_error_trace(err);
521 /* If the callbacks told us to stop, then we're done for now. */
522 if (stop)
523 break;
526 svn_pool_destroy(iterpool);
527 return SVN_NO_ERROR;
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)
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;
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)
548 if (len == 0)
549 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, NULL);
551 *amt = 0;
553 while (len > 0)
555 apr_size_t copy_amt;
557 if (reader->save_len > 0)
559 /* We have some saved content, so use this first. */
561 if (len < reader->save_len)
562 copy_amt = len;
563 else
564 copy_amt = reader->save_len;
566 memcpy(data, reader->save_ptr + reader->save_pos, copy_amt);
567 reader->save_pos += copy_amt;
568 reader->save_len -= copy_amt;
570 else
572 /* No saved content. We should now copy from spillbuf-provided
573 buffers of content. */
575 /* We may need more content from the spillbuf. */
576 if (reader->sb_len == 0)
578 SVN_ERR(svn_spillbuf__read(&reader->sb_ptr, &reader->sb_len,
579 reader->buf,
580 scratch_pool));
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)
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;
593 if (len < reader->sb_len)
594 copy_amt = len;
595 else
596 copy_amt = reader->sb_len;
598 memcpy(data, reader->sb_ptr, copy_amt);
599 reader->sb_ptr += copy_amt;
600 reader->sb_len -= copy_amt;
603 data += copy_amt;
604 len -= copy_amt;
605 (*amt) += copy_amt;
608 return SVN_NO_ERROR;
612 svn_error_t *
613 svn_spillbuf__reader_getc(char *c,
614 svn_spillbuf_reader_t *reader,
615 apr_pool_t *scratch_pool)
617 apr_size_t amt;
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);
623 return SVN_NO_ERROR;
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)
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)
637 if (reader->save_ptr == NULL)
638 reader->save_ptr = apr_palloc(reader->buf->pool,
639 reader->buf->blocksize);
641 memcpy(reader->save_ptr, reader->sb_ptr, reader->sb_len);
642 reader->save_len = reader->sb_len;
643 reader->save_pos = 0;
645 /* No more content in the spillbuf-borrowed buffer. */
646 reader->sb_len = 0;
649 return svn_error_trace(svn_spillbuf__write(reader->buf, data, len,
650 scratch_pool));
654 struct spillbuf_baton
656 svn_spillbuf_reader_t *reader;
657 apr_pool_t *scratch_pool;
661 static svn_error_t *
662 read_handler_spillbuf(void *baton, char *buffer, apr_size_t *len)
664 struct spillbuf_baton *sb = baton;
666 SVN_ERR(svn_spillbuf__reader_read(len, sb->reader, buffer, *len,
667 sb->scratch_pool));
669 svn_pool_clear(sb->scratch_pool);
670 return SVN_NO_ERROR;
674 static svn_error_t *
675 write_handler_spillbuf(void *baton, const char *data, apr_size_t *len)
677 struct spillbuf_baton *sb = baton;
679 SVN_ERR(svn_spillbuf__reader_write(sb->reader, data, *len,
680 sb->scratch_pool));
682 svn_pool_clear(sb->scratch_pool);
683 return SVN_NO_ERROR;
687 svn_stream_t *
688 svn_stream__from_spillbuf(svn_spillbuf_t *buf,
689 apr_pool_t *result_pool)
691 svn_stream_t *stream;
692 struct spillbuf_baton *sb = apr_palloc(result_pool, sizeof(*sb));
694 sb->reader = apr_pcalloc(result_pool, sizeof(*sb->reader));
695 sb->reader->buf = buf;
696 sb->scratch_pool = svn_pool_create(result_pool);
698 stream = svn_stream_create(sb, result_pool);
700 svn_stream_set_read2(stream, NULL /* only full read support */,
701 read_handler_spillbuf);
702 svn_stream_set_write(stream, write_handler_spillbuf);
704 return stream;