Merge branch 'nd/attr-pathspec-fix' into maint
[git] / vcs-svn / sliding_window.c
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_errno("cannot read delta preimage");
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
34 static int check_offset_overflow(off_t offset, uintmax_t len)
35 {
36         if (len > maximum_signed_value_of_type(off_t))
37                 return error("unrepresentable length in delta: "
38                                 "%"PRIuMAX" > OFF_MAX", len);
39         if (signed_add_overflows(offset, (off_t) len))
40                 return error("unrepresentable offset in delta: "
41                                 "%"PRIuMAX" + %"PRIuMAX" > OFF_MAX",
42                                 (uintmax_t) offset, len);
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);
51         assert(!check_offset_overflow(view->off, view->buf.len));
52
53         if (check_offset_overflow(off, width))
54                 return -1;
55         if (off < view->off || off + width < view->off + view->width)
56                 return error("invalid delta: window slides left");
57         if (view->max_off >= 0 && view->max_off < off + (off_t) width)
58                 return error("delta preimage ends early");
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 }