aboutsummaryrefslogtreecommitdiff
path: root/sha1_file.c
diff options
context:
space:
mode:
authorShawn O. Pearce <spearce@spearce.org>2006-12-23 02:34:28 -0500
committerJunio C Hamano <junkio@cox.net>2006-12-29 11:36:45 -0800
commit60bb8b1453e2f93d13e7bb44e8e46c085d2dd752 (patch)
tree3203b1cd4a7479e0175a4c65462ea714ccdabd9b /sha1_file.c
parent54044bf825d311751e30552248be1e0cac99a5a3 (diff)
downloadgit-60bb8b1453e2f93d13e7bb44e8e46c085d2dd752.tar.gz
git-60bb8b1453e2f93d13e7bb44e8e46c085d2dd752.tar.xz
Fully activate the sliding window pack access.
This finally turns on the sliding window behavior for packfile data access by mapping limited size windows and chaining them under the packed_git->windows list. We consider a given byte offset to be within the window only if there would be at least 20 bytes (one hash worth of data) accessible after the requested offset. This range selection relates to the contract that use_pack() makes with its callers, allowing them to access one hash or one object header without needing to call use_pack() for every byte of data obtained. In the worst case scenario we will map the same page of data twice into memory: once at the end of one window and once again at the start of the next window. This duplicate page mapping will happen only when an object header or a delta base reference is spanned over the end of a window and is always limited to just one page of duplication, as no sane operating system will ever have a page size smaller than a hash. I am assuming that the possible wasted page of virtual address space is going to perform faster than the alternatives, which would be to copy the object header or ref delta into a temporary buffer prior to parsing, or to check the window range on every byte during header parsing. We may decide to revisit this decision in the future since this is just a gut instinct decision and has not actually been proven out by experimental testing. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <junkio@cox.net>
Diffstat (limited to 'sha1_file.c')
-rw-r--r--sha1_file.c66
1 files changed, 53 insertions, 13 deletions
diff --git a/sha1_file.c b/sha1_file.c
index 8e14a5a88..548535c84 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -397,8 +397,9 @@ static char *find_sha1_file(const unsigned char *sha1, struct stat *st)
return NULL;
}
-static int pack_used_ctr;
-static unsigned long pack_mapped;
+static unsigned int pack_used_ctr;
+static size_t pack_mapped;
+static size_t page_size;
struct packed_git *packed_git;
static int check_packed_git_idx(const char *path, unsigned long *idx_size_,
@@ -536,31 +537,70 @@ static void open_packed_git(struct packed_git *p)
die("packfile %s does not match index", p->pack_name);
}
+static int in_window(struct pack_window *win, unsigned long offset)
+{
+ /* We must promise at least 20 bytes (one hash) after the
+ * offset is available from this window, otherwise the offset
+ * is not actually in this window and a different window (which
+ * has that one hash excess) must be used. This is to support
+ * the object header and delta base parsing routines below.
+ */
+ off_t win_off = win->offset;
+ return win_off <= offset
+ && (offset + 20) <= (win_off + win->len);
+}
+
unsigned char* use_pack(struct packed_git *p,
struct pack_window **w_cursor,
unsigned long offset,
unsigned int *left)
{
- struct pack_window *win = p->windows;
+ struct pack_window *win = *w_cursor;
if (p->pack_fd == -1)
open_packed_git(p);
- if (!win) {
- pack_mapped += p->pack_size;
- while (packed_git_limit < pack_mapped && unuse_one_window())
- ; /* nothing */
- win = xcalloc(1, sizeof(*win));
- win->len = p->pack_size;
- win->base = mmap(NULL, p->pack_size, PROT_READ, MAP_PRIVATE, p->pack_fd, 0);
- if (win->base == MAP_FAILED)
- die("packfile %s cannot be mapped.", p->pack_name);
- p->windows = win;
+
+ /* Since packfiles end in a hash of their content and its
+ * pointless to ask for an offset into the middle of that
+ * hash, and the in_window function above wouldn't match
+ * don't allow an offset too close to the end of the file.
+ */
+ if (offset > (p->pack_size - 20))
+ die("offset beyond end of packfile (truncated pack?)");
+
+ if (!win || !in_window(win, offset)) {
+ if (win)
+ win->inuse_cnt--;
+ for (win = p->windows; win; win = win->next) {
+ if (in_window(win, offset))
+ break;
+ }
+ if (!win) {
+ if (!page_size)
+ page_size = getpagesize();
+ win = xcalloc(1, sizeof(*win));
+ win->offset = (offset / page_size) * page_size;
+ win->len = p->pack_size - win->offset;
+ if (win->len > packed_git_window_size)
+ win->len = packed_git_window_size;
+ pack_mapped += win->len;
+ while (packed_git_limit < pack_mapped && unuse_one_window())
+ ; /* nothing */
+ win->base = mmap(NULL, win->len,
+ PROT_READ, MAP_PRIVATE,
+ p->pack_fd, win->offset);
+ if (win->base == MAP_FAILED)
+ die("packfile %s cannot be mapped.", p->pack_name);
+ win->next = p->windows;
+ p->windows = win;
+ }
}
if (win != *w_cursor) {
win->last_used = pack_used_ctr++;
win->inuse_cnt++;
*w_cursor = win;
}
+ offset -= win->offset;
if (left)
*left = win->len - offset;
return win->base + offset;