Jonathan Nieder | 9d2f5dd | 2011-01-02 21:54:58 -0600 | [diff] [blame] | 1 | /* |
| 2 | * Licensed under a two-clause BSD-style license. |
| 3 | * See LICENSE for details. |
| 4 | */ |
| 5 | |
| 6 | #include "git-compat-util.h" |
| 7 | #include "sliding_window.h" |
| 8 | #include "line_buffer.h" |
| 9 | #include "strbuf.h" |
| 10 | |
| 11 | static int input_error(struct line_buffer *file) |
| 12 | { |
| 13 | if (!buffer_ferror(file)) |
| 14 | return error("delta preimage ends early"); |
| 15 | return error("cannot read delta preimage: %s", strerror(errno)); |
| 16 | } |
| 17 | |
| 18 | static int skip_or_whine(struct line_buffer *file, off_t gap) |
| 19 | { |
| 20 | if (buffer_skip_bytes(file, gap) != gap) |
| 21 | return input_error(file); |
| 22 | return 0; |
| 23 | } |
| 24 | |
| 25 | static int read_to_fill_or_whine(struct line_buffer *file, |
| 26 | struct strbuf *buf, size_t width) |
| 27 | { |
| 28 | buffer_read_binary(file, buf, width - buf->len); |
| 29 | if (buf->len != width) |
| 30 | return input_error(file); |
| 31 | return 0; |
| 32 | } |
| 33 | |
Jonathan Nieder | 5b8bf02 | 2012-02-02 04:28:02 -0600 | [diff] [blame] | 34 | static int check_offset_overflow(off_t offset, uintmax_t len) |
Jonathan Nieder | 9d2f5dd | 2011-01-02 21:54:58 -0600 | [diff] [blame] | 35 | { |
Ramsay Jones | ce8ebcd | 2012-02-02 03:57:23 -0600 | [diff] [blame] | 36 | if (len > maximum_signed_value_of_type(off_t)) |
Jonathan Nieder | 9d2f5dd | 2011-01-02 21:54:58 -0600 | [diff] [blame] | 37 | return error("unrepresentable length in delta: " |
Jonathan Nieder | 5b8bf02 | 2012-02-02 04:28:02 -0600 | [diff] [blame] | 38 | "%"PRIuMAX" > OFF_MAX", len); |
Ramsay Jones | ce8ebcd | 2012-02-02 03:57:23 -0600 | [diff] [blame] | 39 | if (signed_add_overflows(offset, (off_t) len)) |
Jonathan Nieder | 9d2f5dd | 2011-01-02 21:54:58 -0600 | [diff] [blame] | 40 | return error("unrepresentable offset in delta: " |
| 41 | "%"PRIuMAX" + %"PRIuMAX" > OFF_MAX", |
Jonathan Nieder | 5b8bf02 | 2012-02-02 04:28:02 -0600 | [diff] [blame] | 42 | (uintmax_t) offset, len); |
Jonathan Nieder | 9d2f5dd | 2011-01-02 21:54:58 -0600 | [diff] [blame] | 43 | return 0; |
| 44 | } |
| 45 | |
| 46 | int move_window(struct sliding_view *view, off_t off, size_t width) |
| 47 | { |
| 48 | off_t file_offset; |
| 49 | assert(view); |
| 50 | assert(view->width <= view->buf.len); |
Ramsay Jones | ce8ebcd | 2012-02-02 03:57:23 -0600 | [diff] [blame] | 51 | assert(!check_offset_overflow(view->off, view->buf.len)); |
Jonathan Nieder | 9d2f5dd | 2011-01-02 21:54:58 -0600 | [diff] [blame] | 52 | |
Ramsay Jones | ce8ebcd | 2012-02-02 03:57:23 -0600 | [diff] [blame] | 53 | if (check_offset_overflow(off, width)) |
Jonathan Nieder | 9d2f5dd | 2011-01-02 21:54:58 -0600 | [diff] [blame] | 54 | return -1; |
| 55 | if (off < view->off || off + width < view->off + view->width) |
| 56 | return error("invalid delta: window slides left"); |
David Barr | c68038eff | 2012-06-01 00:41:29 +1000 | [diff] [blame] | 57 | if (view->max_off >= 0 && view->max_off < off + (off_t) width) |
Jonathan Nieder | fbdd4f6 | 2011-05-27 04:07:44 -0500 | [diff] [blame] | 58 | return error("delta preimage ends early"); |
Jonathan Nieder | 9d2f5dd | 2011-01-02 21:54:58 -0600 | [diff] [blame] | 59 | |
| 60 | file_offset = view->off + view->buf.len; |
| 61 | if (off < file_offset) { |
| 62 | /* Move the overlapping region into place. */ |
| 63 | strbuf_remove(&view->buf, 0, off - view->off); |
| 64 | } else { |
| 65 | /* Seek ahead to skip the gap. */ |
| 66 | if (skip_or_whine(view->file, off - file_offset)) |
| 67 | return -1; |
| 68 | strbuf_setlen(&view->buf, 0); |
| 69 | } |
| 70 | |
| 71 | if (view->buf.len > width) |
| 72 | ; /* Already read. */ |
| 73 | else if (read_to_fill_or_whine(view->file, &view->buf, width)) |
| 74 | return -1; |
| 75 | |
| 76 | view->off = off; |
| 77 | view->width = width; |
| 78 | return 0; |
| 79 | } |