mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-29 12:24:11 +00:00
Factor out the mmap window code
This code is useful for more things than just the packfile handling code. Factor it out so it can be reused. Signed-off-by: Carlos Martín Nieto <carlos@cmartin.tk>
This commit is contained in:
parent
03d88ed415
commit
7bfdb3d22b
271
src/mwindow.c
Normal file
271
src/mwindow.c
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
* This file is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License, version 2,
|
||||||
|
* as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* In addition to the permissions in the GNU General Public License,
|
||||||
|
* the authors give you unlimited permission to link the compiled
|
||||||
|
* version of this file into combinations with other programs,
|
||||||
|
* and to distribute those combinations without any restriction
|
||||||
|
* coming from the use of this file. (The General Public License
|
||||||
|
* restrictions do apply in other respects; for example, they cover
|
||||||
|
* modification of the file, and distribution when not linked into
|
||||||
|
* a combined executable.)
|
||||||
|
*
|
||||||
|
* This file is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "mwindow.h"
|
||||||
|
#include "vector.h"
|
||||||
|
#include "fileops.h"
|
||||||
|
#include "map.h"
|
||||||
|
|
||||||
|
#define DEFAULT_WINDOW_SIZE \
|
||||||
|
(sizeof(void*) >= 8 \
|
||||||
|
? 1 * 1024 * 1024 * 1024 \
|
||||||
|
: 32 * 1024 * 1024)
|
||||||
|
|
||||||
|
#define DEFAULT_MAPPED_LIMIT \
|
||||||
|
((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need this because each process is only allowed a specific amount
|
||||||
|
* of memory. Making it writable should generate one instance per
|
||||||
|
* process, but we still need to set a couple of variables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static git_mwindow_ctl ctl = {
|
||||||
|
.window_size = DEFAULT_WINDOW_SIZE,
|
||||||
|
.mapped_limit = DEFAULT_MAPPED_LIMIT
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free all the windows in a sequence, typically because we're done
|
||||||
|
* with the file
|
||||||
|
*/
|
||||||
|
void git_mwindow_free_all(git_mwindow_file *mwf)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
/*
|
||||||
|
* Remove these windows from the global list
|
||||||
|
*/
|
||||||
|
for (i = 0; i < ctl.windowfiles.length; ++i){
|
||||||
|
if (git_vector_get(&ctl.windowfiles, i) == mwf) {
|
||||||
|
git_vector_remove(&ctl.windowfiles, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctl.windowfiles.length == 0) {
|
||||||
|
git_vector_free(&ctl.windowfiles);
|
||||||
|
ctl.windowfiles.contents = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (mwf->windows) {
|
||||||
|
git_mwindow *w = mwf->windows;
|
||||||
|
assert(w->inuse_cnt == 0);
|
||||||
|
|
||||||
|
ctl.mapped -= w->window_map.len;
|
||||||
|
ctl.open_windows--;
|
||||||
|
|
||||||
|
git_futils_mmap_free(&w->window_map);
|
||||||
|
|
||||||
|
mwf->windows = w->next;
|
||||||
|
free(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a window 'win' contains the address 'offset'
|
||||||
|
*/
|
||||||
|
int git_mwindow_contains(git_mwindow *win, off_t offset)
|
||||||
|
{
|
||||||
|
off_t win_off = win->offset;
|
||||||
|
return win_off <= offset
|
||||||
|
&& offset <= (off_t)(win_off + win->window_map.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the least-recently-used window in a file
|
||||||
|
*/
|
||||||
|
void git_mwindow_scan_lru(
|
||||||
|
git_mwindow_file *mwf,
|
||||||
|
git_mwindow **lru_w,
|
||||||
|
git_mwindow **lru_l)
|
||||||
|
{
|
||||||
|
git_mwindow *w, *w_l;
|
||||||
|
|
||||||
|
for (w_l = NULL, w = mwf->windows; w; w = w->next) {
|
||||||
|
if (!w->inuse_cnt) {
|
||||||
|
/*
|
||||||
|
* If the current one is more recent than the last one,
|
||||||
|
* store it in the output parameter. If lru_w is NULL,
|
||||||
|
* it's the first loop, so store it as well.
|
||||||
|
*/
|
||||||
|
if (!*lru_w || w->last_used < (*lru_w)->last_used) {
|
||||||
|
*lru_w = w;
|
||||||
|
*lru_l = w_l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w_l = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close the least recently used window. You should check to see if
|
||||||
|
* the file descriptors need closing from time to time.
|
||||||
|
*/
|
||||||
|
int git_mwindow_close_lru(git_mwindow_file *mwf)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
git_mwindow *lru_w = NULL, *lru_l = NULL;
|
||||||
|
|
||||||
|
/* FIMXE: Does this give us any advantage? */
|
||||||
|
if(mwf->windows)
|
||||||
|
git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
|
||||||
|
|
||||||
|
for (i = 0; i < ctl.windowfiles.length; ++i) {
|
||||||
|
git_mwindow_scan_lru(git_vector_get(&ctl.windowfiles, i), &lru_w, &lru_l);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lru_w) {
|
||||||
|
git_mwindow_close(&lru_w);
|
||||||
|
ctl.mapped -= lru_w->window_map.len;
|
||||||
|
git_futils_mmap_free(&lru_w->window_map);
|
||||||
|
|
||||||
|
if (lru_l)
|
||||||
|
lru_l->next = lru_w->next;
|
||||||
|
else
|
||||||
|
mwf->windows = lru_w->next;
|
||||||
|
|
||||||
|
free(lru_w);
|
||||||
|
ctl.open_windows--;
|
||||||
|
|
||||||
|
return GIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return git__throw(GIT_ERROR, "Failed to close memory window. Couln't find LRU");
|
||||||
|
}
|
||||||
|
|
||||||
|
static git_mwindow *new_window(git_mwindow_file *mwf, git_file fd, size_t size, off_t offset)
|
||||||
|
{
|
||||||
|
size_t walign = ctl.window_size / 2;
|
||||||
|
size_t len;
|
||||||
|
git_mwindow *w;
|
||||||
|
|
||||||
|
w = git__malloc(sizeof(*w));
|
||||||
|
if (w == NULL)
|
||||||
|
return w;
|
||||||
|
|
||||||
|
memset(w, 0x0, sizeof(*w));
|
||||||
|
w->offset = (offset / walign) * walign;
|
||||||
|
|
||||||
|
len = size - w->offset;
|
||||||
|
if (len > ctl.window_size)
|
||||||
|
len = ctl.window_size;
|
||||||
|
|
||||||
|
ctl.mapped += len;
|
||||||
|
|
||||||
|
while(ctl.mapped_limit < ctl.mapped &&
|
||||||
|
git_mwindow_close_lru(mwf) == GIT_SUCCESS) {}
|
||||||
|
|
||||||
|
/* FIXME: Shouldn't we error out if there's an error in closing lru? */
|
||||||
|
|
||||||
|
if (git_futils_mmap_ro(&w->window_map, fd, w->offset, len) < GIT_SUCCESS)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
ctl.mmap_calls++;
|
||||||
|
ctl.open_windows++;
|
||||||
|
|
||||||
|
if (ctl.mapped > ctl.peak_mapped)
|
||||||
|
ctl.peak_mapped = ctl.mapped;
|
||||||
|
|
||||||
|
if (ctl.open_windows > ctl.peak_open_windows)
|
||||||
|
ctl.peak_open_windows = ctl.open_windows;
|
||||||
|
|
||||||
|
return w;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
free(w);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open a new window, closing the least recenty used until we have
|
||||||
|
* enough space. Don't forget to add it to your list
|
||||||
|
*/
|
||||||
|
unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_file fd,
|
||||||
|
size_t size, off_t offset, int extra, unsigned int *left)
|
||||||
|
{
|
||||||
|
git_mwindow *w = *cursor;
|
||||||
|
|
||||||
|
if (!w || !git_mwindow_contains(w, offset + extra)) {
|
||||||
|
if (w) {
|
||||||
|
w->inuse_cnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (w = mwf->windows; w; w = w->next) {
|
||||||
|
if (git_mwindow_contains(w, offset + extra))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there isn't a suitable window, we need to create a new
|
||||||
|
* one.
|
||||||
|
*/
|
||||||
|
if (!w) {
|
||||||
|
w = new_window(mwf, fd, size, offset);
|
||||||
|
if (w == NULL)
|
||||||
|
return NULL;
|
||||||
|
w->next = mwf->windows;
|
||||||
|
mwf->windows = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we changed w, store it in the cursor */
|
||||||
|
if (w != *cursor) {
|
||||||
|
w->last_used = ctl.used_ctr++;
|
||||||
|
w->inuse_cnt++;
|
||||||
|
*cursor = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset -= w->offset;
|
||||||
|
assert(git__is_sizet(offset));
|
||||||
|
|
||||||
|
if (left)
|
||||||
|
*left = w->window_map.len - offset;
|
||||||
|
|
||||||
|
return (unsigned char *) w->window_map.data + offset;
|
||||||
|
|
||||||
|
free(w);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_mwindow_file_register(git_mwindow_file *mwf)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (ctl.windowfiles.length == 0 &&
|
||||||
|
(error = git_vector_init(&ctl.windowfiles, 8, NULL)) < GIT_SUCCESS)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return git_vector_insert(&ctl.windowfiles, mwf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_mwindow_close(git_mwindow **window)
|
||||||
|
{
|
||||||
|
git_mwindow *w = *window;
|
||||||
|
if (w) {
|
||||||
|
w->inuse_cnt--;
|
||||||
|
*window = NULL;
|
||||||
|
}
|
||||||
|
}
|
64
src/mwindow.h
Normal file
64
src/mwindow.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* This file is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License, version 2,
|
||||||
|
* as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* In addition to the permissions in the GNU General Public License,
|
||||||
|
* the authors give you unlimited permission to link the compiled
|
||||||
|
* version of this file into combinations with other programs,
|
||||||
|
* and to distribute those combinations without any restriction
|
||||||
|
* coming from the use of this file. (The General Public License
|
||||||
|
* restrictions do apply in other respects; for example, they cover
|
||||||
|
* modification of the file, and distribution when not linked into
|
||||||
|
* a combined executable.)
|
||||||
|
*
|
||||||
|
* This file is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDE_mwindow__
|
||||||
|
#define INCLUDE_mwindow__
|
||||||
|
|
||||||
|
#include "map.h"
|
||||||
|
#include "vector.h"
|
||||||
|
#include "fileops.h"
|
||||||
|
|
||||||
|
typedef struct git_mwindow {
|
||||||
|
struct git_mwindow *next;
|
||||||
|
git_map window_map;
|
||||||
|
off_t offset;
|
||||||
|
unsigned int last_used;
|
||||||
|
unsigned int inuse_cnt;
|
||||||
|
} git_mwindow;
|
||||||
|
|
||||||
|
typedef struct git_mwindow_file {
|
||||||
|
git_mwindow *windows;
|
||||||
|
} git_mwindow_file;
|
||||||
|
|
||||||
|
typedef struct git_mwindow_ctl {
|
||||||
|
size_t mapped;
|
||||||
|
unsigned int open_windows;
|
||||||
|
size_t window_size; /* needs default value */
|
||||||
|
size_t mapped_limit; /* needs default value */
|
||||||
|
unsigned int mmap_calls;
|
||||||
|
unsigned int peak_open_windows;
|
||||||
|
size_t peak_mapped;
|
||||||
|
size_t used_ctr;
|
||||||
|
git_vector windowfiles;
|
||||||
|
} git_mwindow_ctl;
|
||||||
|
|
||||||
|
int git_mwindow_contains(git_mwindow *win, off_t offset);
|
||||||
|
void git_mwindow_free_all(git_mwindow_file *mwf);
|
||||||
|
unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_file fd, size_t size, off_t offset, int extra, unsigned int *left);
|
||||||
|
void git_mwindow_scan_lru(git_mwindow_file *mwf, git_mwindow **lru_w, git_mwindow **lru_l);
|
||||||
|
int git_mwindow_file_register(git_mwindow_file *mwf);
|
||||||
|
void git_mwindow_close(git_mwindow **w_cursor);
|
||||||
|
|
||||||
|
#endif
|
264
src/odb_pack.c
264
src/odb_pack.c
@ -32,17 +32,10 @@
|
|||||||
#include "odb.h"
|
#include "odb.h"
|
||||||
#include "delta-apply.h"
|
#include "delta-apply.h"
|
||||||
#include "sha1_lookup.h"
|
#include "sha1_lookup.h"
|
||||||
|
#include "mwindow.h"
|
||||||
|
|
||||||
#include "git2/odb_backend.h"
|
#include "git2/odb_backend.h"
|
||||||
|
|
||||||
#define DEFAULT_WINDOW_SIZE \
|
|
||||||
(sizeof(void*) >= 8 \
|
|
||||||
? 1 * 1024 * 1024 * 1024 \
|
|
||||||
: 32 * 1024 * 1024)
|
|
||||||
|
|
||||||
#define DEFAULT_MAPPED_LIMIT \
|
|
||||||
((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
|
|
||||||
|
|
||||||
#define PACK_SIGNATURE 0x5041434b /* "PACK" */
|
#define PACK_SIGNATURE 0x5041434b /* "PACK" */
|
||||||
#define PACK_VERSION 2
|
#define PACK_VERSION 2
|
||||||
#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
|
#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
|
||||||
@ -76,18 +69,11 @@ struct pack_idx_header {
|
|||||||
uint32_t idx_version;
|
uint32_t idx_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pack_window {
|
|
||||||
struct pack_window *next;
|
|
||||||
git_map window_map;
|
|
||||||
off_t offset;
|
|
||||||
unsigned int last_used;
|
|
||||||
unsigned int inuse_cnt;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct pack_file {
|
struct pack_file {
|
||||||
struct pack_window *windows;
|
int pack_fd;
|
||||||
|
git_mwindow_file mwf;
|
||||||
|
//git_mwindow *windows;
|
||||||
off_t pack_size;
|
off_t pack_size;
|
||||||
|
|
||||||
git_map index_map;
|
git_map index_map;
|
||||||
|
|
||||||
uint32_t num_objects;
|
uint32_t num_objects;
|
||||||
@ -96,7 +82,6 @@ struct pack_file {
|
|||||||
|
|
||||||
int index_version;
|
int index_version;
|
||||||
git_time_t mtime;
|
git_time_t mtime;
|
||||||
int pack_fd;
|
|
||||||
unsigned pack_local:1, pack_keep:1;
|
unsigned pack_local:1, pack_keep:1;
|
||||||
git_oid sha1;
|
git_oid sha1;
|
||||||
|
|
||||||
@ -116,19 +101,6 @@ struct pack_backend {
|
|||||||
struct pack_file *last_found;
|
struct pack_file *last_found;
|
||||||
char *pack_folder;
|
char *pack_folder;
|
||||||
time_t pack_folder_mtime;
|
time_t pack_folder_mtime;
|
||||||
|
|
||||||
size_t window_size; /* needs default value */
|
|
||||||
|
|
||||||
size_t mapped_limit; /* needs default value */
|
|
||||||
size_t peak_mapped;
|
|
||||||
size_t mapped;
|
|
||||||
|
|
||||||
size_t used_ctr;
|
|
||||||
|
|
||||||
unsigned int peak_open_windows;
|
|
||||||
unsigned int open_windows;
|
|
||||||
|
|
||||||
unsigned int mmap_calls;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,8 +198,6 @@ struct pack_backend {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************
|
/***********************************************************
|
||||||
*
|
*
|
||||||
* FORWARD DECLARATIONS
|
* FORWARD DECLARATIONS
|
||||||
@ -235,19 +205,10 @@ struct pack_backend {
|
|||||||
***********************************************************/
|
***********************************************************/
|
||||||
|
|
||||||
static void pack_window_free_all(struct pack_backend *backend, struct pack_file *p);
|
static void pack_window_free_all(struct pack_backend *backend, struct pack_file *p);
|
||||||
static int pack_window_contains(struct pack_window *win, off_t offset);
|
static int pack_window_contains(git_mwindow *win, off_t offset);
|
||||||
|
|
||||||
static void pack_window_scan_lru(struct pack_file *p, struct pack_file **lru_p,
|
static unsigned char *pack_window_open(struct pack_file *p,
|
||||||
struct pack_window **lru_w, struct pack_window **lru_l);
|
git_mwindow **w_cursor, off_t offset, unsigned int *left);
|
||||||
|
|
||||||
static int pack_window_close_lru( struct pack_backend *backend,
|
|
||||||
struct pack_file *current, git_file keep_fd);
|
|
||||||
|
|
||||||
static void pack_window_close(struct pack_window **w_cursor);
|
|
||||||
|
|
||||||
static unsigned char *pack_window_open( struct pack_backend *backend,
|
|
||||||
struct pack_file *p, struct pack_window **w_cursor, off_t offset,
|
|
||||||
unsigned int *left);
|
|
||||||
|
|
||||||
static int packfile_sort__cb(const void *a_, const void *b_);
|
static int packfile_sort__cb(const void *a_, const void *b_);
|
||||||
|
|
||||||
@ -299,8 +260,7 @@ static int pack_entry_find_prefix(struct pack_entry *e,
|
|||||||
const git_oid *short_oid,
|
const git_oid *short_oid,
|
||||||
unsigned int len);
|
unsigned int len);
|
||||||
|
|
||||||
static off_t get_delta_base(struct pack_backend *backend,
|
static off_t get_delta_base(struct pack_file *p, git_mwindow **w_curs,
|
||||||
struct pack_file *p, struct pack_window **w_curs,
|
|
||||||
off_t *curpos, git_otype type,
|
off_t *curpos, git_otype type,
|
||||||
off_t delta_obj_offset);
|
off_t delta_obj_offset);
|
||||||
|
|
||||||
@ -313,16 +273,14 @@ static unsigned long packfile_unpack_header1(
|
|||||||
static int packfile_unpack_header(
|
static int packfile_unpack_header(
|
||||||
size_t *size_p,
|
size_t *size_p,
|
||||||
git_otype *type_p,
|
git_otype *type_p,
|
||||||
struct pack_backend *backend,
|
|
||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
struct pack_window **w_curs,
|
git_mwindow **w_curs,
|
||||||
off_t *curpos);
|
off_t *curpos);
|
||||||
|
|
||||||
static int packfile_unpack_compressed(
|
static int packfile_unpack_compressed(
|
||||||
git_rawobj *obj,
|
git_rawobj *obj,
|
||||||
struct pack_backend *backend,
|
|
||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
struct pack_window **w_curs,
|
git_mwindow **w_curs,
|
||||||
off_t curpos,
|
off_t curpos,
|
||||||
size_t size,
|
size_t size,
|
||||||
git_otype type);
|
git_otype type);
|
||||||
@ -331,7 +289,7 @@ static int packfile_unpack_delta(
|
|||||||
git_rawobj *obj,
|
git_rawobj *obj,
|
||||||
struct pack_backend *backend,
|
struct pack_backend *backend,
|
||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
struct pack_window **w_curs,
|
git_mwindow **w_curs,
|
||||||
off_t curpos,
|
off_t curpos,
|
||||||
size_t delta_size,
|
size_t delta_size,
|
||||||
git_otype delta_type,
|
git_otype delta_type,
|
||||||
@ -350,23 +308,12 @@ static int packfile_unpack(git_rawobj *obj, struct pack_backend *backend,
|
|||||||
*
|
*
|
||||||
***********************************************************/
|
***********************************************************/
|
||||||
|
|
||||||
void pack_window_free_all(struct pack_backend *backend, struct pack_file *p)
|
GIT_INLINE(void) pack_window_free_all(struct pack_backend *GIT_UNUSED(backend), struct pack_file *p)
|
||||||
{
|
{
|
||||||
while (p->windows) {
|
git_mwindow_free_all(&p->mwf);
|
||||||
struct pack_window *w = p->windows;
|
|
||||||
assert(w->inuse_cnt == 0);
|
|
||||||
|
|
||||||
backend->mapped -= w->window_map.len;
|
|
||||||
backend->open_windows--;
|
|
||||||
|
|
||||||
git_futils_mmap_free(&w->window_map);
|
|
||||||
|
|
||||||
p->windows = w->next;
|
|
||||||
free(w);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_INLINE(int) pack_window_contains(struct pack_window *win, off_t offset)
|
GIT_INLINE(int) pack_window_contains(git_mwindow *win, off_t offset)
|
||||||
{
|
{
|
||||||
/* We must promise at least 20 bytes (one hash) after the
|
/* We must promise at least 20 bytes (one hash) after the
|
||||||
* offset is available from this window, otherwise the offset
|
* offset is available from this window, otherwise the offset
|
||||||
@ -374,86 +321,15 @@ GIT_INLINE(int) pack_window_contains(struct pack_window *win, off_t offset)
|
|||||||
* has that one hash excess) must be used. This is to support
|
* has that one hash excess) must be used. This is to support
|
||||||
* the object header and delta base parsing routines below.
|
* the object header and delta base parsing routines below.
|
||||||
*/
|
*/
|
||||||
off_t win_off = win->offset;
|
return git_mwindow_contains(win, offset + 20);
|
||||||
return win_off <= offset
|
|
||||||
&& (offset + 20) <= (off_t)(win_off + win->window_map.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pack_window_scan_lru(
|
|
||||||
struct pack_file *p,
|
|
||||||
struct pack_file **lru_p,
|
|
||||||
struct pack_window **lru_w,
|
|
||||||
struct pack_window **lru_l)
|
|
||||||
{
|
|
||||||
struct pack_window *w, *w_l;
|
|
||||||
|
|
||||||
for (w_l = NULL, w = p->windows; w; w = w->next) {
|
|
||||||
if (!w->inuse_cnt) {
|
|
||||||
if (!*lru_w || w->last_used < (*lru_w)->last_used) {
|
|
||||||
*lru_p = p;
|
|
||||||
*lru_w = w;
|
|
||||||
*lru_l = w_l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w_l = w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pack_window_close_lru(
|
|
||||||
struct pack_backend *backend,
|
|
||||||
struct pack_file *current,
|
|
||||||
git_file keep_fd)
|
|
||||||
{
|
|
||||||
struct pack_file *lru_p = NULL;
|
|
||||||
struct pack_window *lru_w = NULL, *lru_l = NULL;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
if (current)
|
|
||||||
pack_window_scan_lru(current, &lru_p, &lru_w, &lru_l);
|
|
||||||
|
|
||||||
for (i = 0; i < backend->packs.length; ++i)
|
|
||||||
pack_window_scan_lru(git_vector_get(&backend->packs, i), &lru_p, &lru_w, &lru_l);
|
|
||||||
|
|
||||||
if (lru_p) {
|
|
||||||
backend->mapped -= lru_w->window_map.len;
|
|
||||||
git_futils_mmap_free(&lru_w->window_map);
|
|
||||||
|
|
||||||
if (lru_l)
|
|
||||||
lru_l->next = lru_w->next;
|
|
||||||
else {
|
|
||||||
lru_p->windows = lru_w->next;
|
|
||||||
if (!lru_p->windows && lru_p->pack_fd != keep_fd) {
|
|
||||||
p_close(lru_p->pack_fd);
|
|
||||||
lru_p->pack_fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(lru_w);
|
|
||||||
backend->open_windows--;
|
|
||||||
return GIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
return git__throw(GIT_ERROR, "Failed to close pack window");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pack_window_close(struct pack_window **w_cursor)
|
|
||||||
{
|
|
||||||
struct pack_window *w = *w_cursor;
|
|
||||||
if (w) {
|
|
||||||
w->inuse_cnt--;
|
|
||||||
*w_cursor = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned char *pack_window_open(
|
static unsigned char *pack_window_open(
|
||||||
struct pack_backend *backend,
|
|
||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
struct pack_window **w_cursor,
|
git_mwindow **w_cursor,
|
||||||
off_t offset,
|
off_t offset,
|
||||||
unsigned int *left)
|
unsigned int *left)
|
||||||
{
|
{
|
||||||
struct pack_window *win = *w_cursor;
|
|
||||||
|
|
||||||
if (p->pack_fd == -1 && packfile_open(p) < GIT_SUCCESS)
|
if (p->pack_fd == -1 && packfile_open(p) < GIT_SUCCESS)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -465,73 +341,8 @@ static unsigned char *pack_window_open(
|
|||||||
if (offset > (p->pack_size - 20))
|
if (offset > (p->pack_size - 20))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!win || !pack_window_contains(win, offset)) {
|
return git_mwindow_open(&p->mwf, w_cursor, p->pack_fd, p->pack_size, offset, 20, left);
|
||||||
|
}
|
||||||
if (win)
|
|
||||||
win->inuse_cnt--;
|
|
||||||
|
|
||||||
for (win = p->windows; win; win = win->next) {
|
|
||||||
if (pack_window_contains(win, offset))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!win) {
|
|
||||||
size_t window_align = backend->window_size / 2;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
win = git__calloc(1, sizeof(*win));
|
|
||||||
if (win == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
win->offset = (offset / window_align) * window_align;
|
|
||||||
|
|
||||||
len = (size_t)(p->pack_size - win->offset);
|
|
||||||
if (len > backend->window_size)
|
|
||||||
len = backend->window_size;
|
|
||||||
|
|
||||||
backend->mapped += len;
|
|
||||||
|
|
||||||
while (backend->mapped_limit < backend->mapped &&
|
|
||||||
pack_window_close_lru(backend, p, p->pack_fd) == GIT_SUCCESS) {}
|
|
||||||
|
|
||||||
if (git_futils_mmap_ro(&win->window_map, p->pack_fd,
|
|
||||||
win->offset, len) < GIT_SUCCESS) {
|
|
||||||
free(win);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->mmap_calls++;
|
|
||||||
backend->open_windows++;
|
|
||||||
|
|
||||||
if (backend->mapped > backend->peak_mapped)
|
|
||||||
backend->peak_mapped = backend->mapped;
|
|
||||||
|
|
||||||
if (backend->open_windows > backend->peak_open_windows)
|
|
||||||
backend->peak_open_windows = backend->open_windows;
|
|
||||||
|
|
||||||
win->next = p->windows;
|
|
||||||
p->windows = win;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (win != *w_cursor) {
|
|
||||||
win->last_used = backend->used_ctr++;
|
|
||||||
win->inuse_cnt++;
|
|
||||||
*w_cursor = win;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset -= win->offset;
|
|
||||||
assert(git__is_sizet(offset));
|
|
||||||
|
|
||||||
if (left)
|
|
||||||
*left = win->window_map.len - (size_t)offset;
|
|
||||||
|
|
||||||
return (unsigned char *)win->window_map.data + offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -766,6 +577,11 @@ static int packfile_open(struct pack_file *p)
|
|||||||
if (p->pack_fd < 0 || p_fstat(p->pack_fd, &st) < GIT_SUCCESS)
|
if (p->pack_fd < 0 || p_fstat(p->pack_fd, &st) < GIT_SUCCESS)
|
||||||
return git__throw(GIT_EOSERR, "Failed to open packfile. File appears to be corrupted");
|
return git__throw(GIT_EOSERR, "Failed to open packfile. File appears to be corrupted");
|
||||||
|
|
||||||
|
if (git_mwindow_file_register(&p->mwf) < GIT_SUCCESS) {
|
||||||
|
p_close(p->pack_fd);
|
||||||
|
return git__throw(GIT_ERROR, "Failed to register packfile windows");
|
||||||
|
}
|
||||||
|
|
||||||
/* If we created the struct before we had the pack we lack size. */
|
/* If we created the struct before we had the pack we lack size. */
|
||||||
if (!p->pack_size) {
|
if (!p->pack_size) {
|
||||||
if (!S_ISREG(st.st_mode))
|
if (!S_ISREG(st.st_mode))
|
||||||
@ -1210,9 +1026,8 @@ static unsigned long packfile_unpack_header1(
|
|||||||
static int packfile_unpack_header(
|
static int packfile_unpack_header(
|
||||||
size_t *size_p,
|
size_t *size_p,
|
||||||
git_otype *type_p,
|
git_otype *type_p,
|
||||||
struct pack_backend *backend,
|
|
||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
struct pack_window **w_curs,
|
git_mwindow **w_curs,
|
||||||
off_t *curpos)
|
off_t *curpos)
|
||||||
{
|
{
|
||||||
unsigned char *base;
|
unsigned char *base;
|
||||||
@ -1225,7 +1040,7 @@ static int packfile_unpack_header(
|
|||||||
* the maximum deflated object size is 2^137, which is just
|
* the maximum deflated object size is 2^137, which is just
|
||||||
* insane, so we know won't exceed what we have been given.
|
* insane, so we know won't exceed what we have been given.
|
||||||
*/
|
*/
|
||||||
base = pack_window_open(backend, p, w_curs, *curpos, &left);
|
base = pack_window_open(p, w_curs, *curpos, &left);
|
||||||
if (base == NULL)
|
if (base == NULL)
|
||||||
return GIT_ENOMEM;
|
return GIT_ENOMEM;
|
||||||
|
|
||||||
@ -1240,9 +1055,8 @@ static int packfile_unpack_header(
|
|||||||
|
|
||||||
static int packfile_unpack_compressed(
|
static int packfile_unpack_compressed(
|
||||||
git_rawobj *obj,
|
git_rawobj *obj,
|
||||||
struct pack_backend *backend,
|
|
||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
struct pack_window **w_curs,
|
git_mwindow **w_curs,
|
||||||
off_t curpos,
|
off_t curpos,
|
||||||
size_t size,
|
size_t size,
|
||||||
git_otype type)
|
git_otype type)
|
||||||
@ -1265,7 +1079,7 @@ static int packfile_unpack_compressed(
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
in = pack_window_open(backend, p, w_curs, curpos, &stream.avail_in);
|
in = pack_window_open(p, w_curs, curpos, &stream.avail_in);
|
||||||
stream.next_in = in;
|
stream.next_in = in;
|
||||||
st = inflate(&stream, Z_FINISH);
|
st = inflate(&stream, Z_FINISH);
|
||||||
|
|
||||||
@ -1289,14 +1103,13 @@ static int packfile_unpack_compressed(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static off_t get_delta_base(
|
static off_t get_delta_base(
|
||||||
struct pack_backend *backend,
|
|
||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
struct pack_window **w_curs,
|
git_mwindow **w_curs,
|
||||||
off_t *curpos,
|
off_t *curpos,
|
||||||
git_otype type,
|
git_otype type,
|
||||||
off_t delta_obj_offset)
|
off_t delta_obj_offset)
|
||||||
{
|
{
|
||||||
unsigned char *base_info = pack_window_open(backend, p, w_curs, *curpos, NULL);
|
unsigned char *base_info = pack_window_open(p, w_curs, *curpos, NULL);
|
||||||
off_t base_offset;
|
off_t base_offset;
|
||||||
git_oid unused;
|
git_oid unused;
|
||||||
|
|
||||||
@ -1336,7 +1149,7 @@ static int packfile_unpack_delta(
|
|||||||
git_rawobj *obj,
|
git_rawobj *obj,
|
||||||
struct pack_backend *backend,
|
struct pack_backend *backend,
|
||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
struct pack_window **w_curs,
|
git_mwindow **w_curs,
|
||||||
off_t curpos,
|
off_t curpos,
|
||||||
size_t delta_size,
|
size_t delta_size,
|
||||||
git_otype delta_type,
|
git_otype delta_type,
|
||||||
@ -1346,11 +1159,11 @@ static int packfile_unpack_delta(
|
|||||||
git_rawobj base, delta;
|
git_rawobj base, delta;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
base_offset = get_delta_base(backend, p, w_curs, &curpos, delta_type, obj_offset);
|
base_offset = get_delta_base(p, w_curs, &curpos, delta_type, obj_offset);
|
||||||
if (base_offset == 0)
|
if (base_offset == 0)
|
||||||
return git__throw(GIT_EOBJCORRUPTED, "Delta offset is zero");
|
return git__throw(GIT_EOBJCORRUPTED, "Delta offset is zero");
|
||||||
|
|
||||||
pack_window_close(w_curs);
|
git_mwindow_close(w_curs);
|
||||||
error = packfile_unpack(&base, backend, p, base_offset);
|
error = packfile_unpack(&base, backend, p, base_offset);
|
||||||
|
|
||||||
/* TODO: git.git tries to load the base from other packfiles
|
/* TODO: git.git tries to load the base from other packfiles
|
||||||
@ -1358,7 +1171,7 @@ static int packfile_unpack_delta(
|
|||||||
if (error < GIT_SUCCESS)
|
if (error < GIT_SUCCESS)
|
||||||
return git__rethrow(error, "Corrupted delta");
|
return git__rethrow(error, "Corrupted delta");
|
||||||
|
|
||||||
error = packfile_unpack_compressed(&delta, backend, p, w_curs, curpos, delta_size, delta_type);
|
error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);
|
||||||
if (error < GIT_SUCCESS) {
|
if (error < GIT_SUCCESS) {
|
||||||
free(base.data);
|
free(base.data);
|
||||||
return git__rethrow(error, "Corrupted delta");
|
return git__rethrow(error, "Corrupted delta");
|
||||||
@ -1383,7 +1196,7 @@ static int packfile_unpack(
|
|||||||
struct pack_file *p,
|
struct pack_file *p,
|
||||||
off_t obj_offset)
|
off_t obj_offset)
|
||||||
{
|
{
|
||||||
struct pack_window *w_curs = NULL;
|
git_mwindow *w_curs = NULL;
|
||||||
off_t curpos = obj_offset;
|
off_t curpos = obj_offset;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
@ -1398,7 +1211,7 @@ static int packfile_unpack(
|
|||||||
obj->len = 0;
|
obj->len = 0;
|
||||||
obj->type = GIT_OBJ_BAD;
|
obj->type = GIT_OBJ_BAD;
|
||||||
|
|
||||||
error = packfile_unpack_header(&size, &type, backend, p, &w_curs, &curpos);
|
error = packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
|
||||||
if (error < GIT_SUCCESS)
|
if (error < GIT_SUCCESS)
|
||||||
return git__rethrow(error, "Failed to unpack packfile");
|
return git__rethrow(error, "Failed to unpack packfile");
|
||||||
|
|
||||||
@ -1415,7 +1228,7 @@ static int packfile_unpack(
|
|||||||
case GIT_OBJ_BLOB:
|
case GIT_OBJ_BLOB:
|
||||||
case GIT_OBJ_TAG:
|
case GIT_OBJ_TAG:
|
||||||
error = packfile_unpack_compressed(
|
error = packfile_unpack_compressed(
|
||||||
obj, backend, p, &w_curs, curpos,
|
obj, p, &w_curs, curpos,
|
||||||
size, type);
|
size, type);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1424,7 +1237,7 @@ static int packfile_unpack(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pack_window_close(&w_curs);
|
git_mwindow_close(&w_curs);
|
||||||
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to unpack packfile");
|
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to unpack packfile");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1551,9 +1364,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
|
|||||||
return GIT_ENOMEM;
|
return GIT_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
backend->window_size = DEFAULT_WINDOW_SIZE;
|
|
||||||
backend->mapped_limit = DEFAULT_MAPPED_LIMIT;
|
|
||||||
|
|
||||||
git_path_join(path, objects_dir, "pack");
|
git_path_join(path, objects_dir, "pack");
|
||||||
if (git_futils_isdir(path) == GIT_SUCCESS) {
|
if (git_futils_isdir(path) == GIT_SUCCESS) {
|
||||||
backend->pack_folder = git__strdup(path);
|
backend->pack_folder = git__strdup(path);
|
||||||
|
Loading…
Reference in New Issue
Block a user