Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
git2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
git2
Commits
bcf9792f
Commit
bcf9792f
authored
May 13, 2014
by
Vicent Marti
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2330 from libgit2/cmn/pack-unpack-loop
Make pack object lookup use loops
parents
e1619626
c968ce2c
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
211 additions
and
78 deletions
+211
-78
src/pack.c
+201
-78
src/pack.h
+10
-0
No files found.
src/pack.c
View file @
bcf9792f
...
@@ -514,72 +514,105 @@ int git_packfile_resolve_header(
...
@@ -514,72 +514,105 @@ int git_packfile_resolve_header(
return
error
;
return
error
;
}
}
static
int
packfile_unpack_delta
(
#define SMALL_STACK_SIZE 64
git_rawobj
*
obj
,
struct
git_pack_file
*
p
,
git_mwindow
**
w_curs
,
git_off_t
*
curpos
,
size_t
delta_size
,
git_otype
delta_type
,
git_off_t
obj_offset
)
{
git_off_t
base_offset
,
base_key
;
git_rawobj
base
,
delta
;
git_pack_cache_entry
*
cached
=
NULL
;
int
error
,
found_base
=
0
;
base_offset
=
get_delta_base
(
p
,
w_curs
,
curpos
,
delta_type
,
obj_offset
);
/**
git_mwindow_close
(
w_curs
);
* Generate the chain of dependencies which we need to get to the
if
(
base_offset
==
0
)
* object at `off`. `chain` is used a stack, popping gives the right
return
packfile_error
(
"delta offset is zero"
);
* order to apply deltas on. If an object is found in the pack's base
if
(
base_offset
<
0
)
/* must actually be an error code */
* cache, we stop calculating there.
return
(
int
)
base_offset
;
*/
static
int
pack_dependency_chain
(
git_dependency_chain
*
chain_out
,
git_pack_cache_entry
**
cached_out
,
git_off_t
*
cached_off
,
struct
pack_chain_elem
*
small_stack
,
size_t
*
stack_sz
,
struct
git_pack_file
*
p
,
git_off_t
obj_offset
)
{
git_dependency_chain
chain
=
GIT_ARRAY_INIT
;
git_mwindow
*
w_curs
=
NULL
;
git_off_t
curpos
=
obj_offset
,
base_offset
;
int
error
=
0
,
use_heap
=
0
;
size_t
size
,
elem_pos
;
git_otype
type
;
if
(
!
p
->
bases
.
entries
&&
(
cache_init
(
&
p
->
bases
)
<
0
))
if
(
!
p
->
bases
.
entries
&&
(
cache_init
(
&
p
->
bases
)
<
0
))
return
-
1
;
return
-
1
;
base_key
=
base_offset
;
/* git_packfile_unpack modifies base_offset */
elem_pos
=
0
;
if
((
cached
=
cache_get
(
&
p
->
bases
,
base_offset
))
!=
NULL
)
{
while
(
true
)
{
memcpy
(
&
base
,
&
cached
->
raw
,
sizeof
(
git_rawobj
));
struct
pack_chain_elem
*
elem
;
found_base
=
1
;
git_pack_cache_entry
*
cached
=
NULL
;
}
if
(
!
cached
)
{
/* have to inflate it */
/* if we have a base cached, we can stop here instead */
error
=
git_packfile_unpack
(
&
base
,
p
,
&
base_offset
);
if
((
cached
=
cache_get
(
&
p
->
bases
,
obj_offset
))
!=
NULL
)
{
*
cached_out
=
cached
;
*
cached_off
=
obj_offset
;
break
;
}
/* if we run out of space on the small stack, use the array */
if
(
elem_pos
==
SMALL_STACK_SIZE
)
{
git_array_init_to_size
(
chain
,
elem_pos
);
GITERR_CHECK_ARRAY
(
chain
);
memcpy
(
chain
.
ptr
,
small_stack
,
elem_pos
*
sizeof
(
struct
pack_chain_elem
));
chain
.
size
=
elem_pos
;
use_heap
=
1
;
}
curpos
=
obj_offset
;
if
(
!
use_heap
)
{
elem
=
&
small_stack
[
elem_pos
];
}
else
{
elem
=
git_array_alloc
(
chain
);
if
(
!
elem
)
{
error
=
-
1
;
goto
on_error
;
}
}
elem
->
base_key
=
obj_offset
;
error
=
git_packfile_unpack_header
(
&
size
,
&
type
,
&
p
->
mwf
,
&
w_curs
,
&
curpos
);
git_mwindow_close
(
&
w_curs
);
/*
* TODO: git.git tries to load the base from other packfiles
* or loose objects.
*
* We'll need to do this in order to support thin packs.
*/
if
(
error
<
0
)
if
(
error
<
0
)
return
error
;
goto
on_error
;
}
error
=
packfile_unpack_compressed
(
&
delta
,
p
,
w_curs
,
curpos
,
delta_size
,
delta_type
);
elem
->
offset
=
curpos
;
git_mwindow_close
(
w_curs
);
elem
->
size
=
size
;
elem
->
type
=
type
;
elem
->
base_key
=
obj_offset
;
if
(
error
<
0
)
{
if
(
type
!=
GIT_OBJ_OFS_DELTA
&&
type
!=
GIT_OBJ_REF_DELTA
)
if
(
!
found_base
)
break
;
git__free
(
base
.
data
);
return
error
;
}
obj
->
type
=
base
.
type
;
base_offset
=
get_delta_base
(
p
,
&
w_curs
,
&
curpos
,
type
,
obj_offset
);
error
=
git__delta_apply
(
obj
,
base
.
data
,
base
.
len
,
delta
.
data
,
delta
.
len
);
git_mwindow_close
(
&
w_curs
);
if
(
error
<
0
)
goto
on_error
;
if
(
found_base
)
if
(
base_offset
==
0
)
{
git_atomic_dec
(
&
cached
->
refcount
);
error
=
packfile_error
(
"delta offset is zero"
);
else
if
(
cache_add
(
&
p
->
bases
,
&
base
,
base_key
)
<
0
)
goto
on_error
;
git__free
(
base
.
data
);
}
if
(
base_offset
<
0
)
{
/* must actually be an error code */
error
=
(
int
)
base_offset
;
goto
on_error
;
}
on_error:
/* we need to pass the pos *after* the delta-base bit */
git__free
(
delta
.
data
)
;
elem
->
offset
=
curpos
;
return
error
;
/* error set by git__delta_apply */
/* go through the loop again, but with the new object */
obj_offset
=
base_offset
;
elem_pos
++
;
}
*
stack_sz
=
elem_pos
+
1
;
*
chain_out
=
chain
;
return
error
;
on_error:
git_array_clear
(
chain
);
return
error
;
}
}
int
git_packfile_unpack
(
int
git_packfile_unpack
(
...
@@ -589,48 +622,138 @@ int git_packfile_unpack(
...
@@ -589,48 +622,138 @@ int git_packfile_unpack(
{
{
git_mwindow
*
w_curs
=
NULL
;
git_mwindow
*
w_curs
=
NULL
;
git_off_t
curpos
=
*
obj_offset
;
git_off_t
curpos
=
*
obj_offset
;
int
error
;
int
error
,
free_base
=
0
;
git_dependency_chain
chain
=
GIT_ARRAY_INIT
;
size_t
size
=
0
;
struct
pack_chain_elem
*
elem
=
NULL
,
*
stack
;
git_otype
type
;
git_pack_cache_entry
*
cached
=
NULL
;
struct
pack_chain_elem
small_stack
[
SMALL_STACK_SIZE
];
size_t
stack_size
,
elem_pos
;
git_otype
base_type
;
/*
/*
* TODO: optionally check the CRC on the packfile
* TODO: optionally check the CRC on the packfile
*/
*/
error
=
pack_dependency_chain
(
&
chain
,
&
cached
,
obj_offset
,
small_stack
,
&
stack_size
,
p
,
*
obj_offset
);
if
(
error
<
0
)
return
error
;
obj
->
data
=
NULL
;
obj
->
data
=
NULL
;
obj
->
len
=
0
;
obj
->
len
=
0
;
obj
->
type
=
GIT_OBJ_BAD
;
obj
->
type
=
GIT_OBJ_BAD
;
error
=
git_packfile_unpack_header
(
&
size
,
&
type
,
&
p
->
mwf
,
&
w_curs
,
&
curpos
);
/* let's point to the right stack */
git_mwindow_close
(
&
w_curs
)
;
stack
=
chain
.
ptr
?
chain
.
ptr
:
small_stack
;
if
(
error
<
0
)
elem_pos
=
stack_size
;
return
error
;
if
(
cached
)
{
memcpy
(
obj
,
&
cached
->
raw
,
sizeof
(
git_rawobj
));
base_type
=
obj
->
type
;
elem_pos
--
;
/* stack_size includes the base, which isn't actually there */
}
else
{
elem
=
&
stack
[
--
elem_pos
];
base_type
=
elem
->
type
;
}
switch
(
type
)
{
if
(
error
<
0
)
case
GIT_OBJ_OFS_DELTA
:
goto
cleanup
;
case
GIT_OBJ_REF_DELTA
:
error
=
packfile_unpack_delta
(
obj
,
p
,
&
w_curs
,
&
curpos
,
size
,
type
,
*
obj_offset
);
break
;
switch
(
base_type
)
{
case
GIT_OBJ_COMMIT
:
case
GIT_OBJ_COMMIT
:
case
GIT_OBJ_TREE
:
case
GIT_OBJ_TREE
:
case
GIT_OBJ_BLOB
:
case
GIT_OBJ_BLOB
:
case
GIT_OBJ_TAG
:
case
GIT_OBJ_TAG
:
error
=
packfile_unpack_compressed
(
if
(
!
cached
)
{
obj
,
p
,
&
w_curs
,
&
curpos
,
curpos
=
elem
->
offset
;
size
,
type
);
error
=
packfile_unpack_compressed
(
obj
,
p
,
&
w_curs
,
&
curpos
,
elem
->
size
,
elem
->
type
);
git_mwindow_close
(
&
w_curs
);
base_type
=
elem
->
type
;
}
if
(
error
<
0
)
goto
cleanup
;
break
;
break
;
case
GIT_OBJ_OFS_DELTA
:
case
GIT_OBJ_REF_DELTA
:
error
=
packfile_error
(
"dependency chain ends in a delta"
);
goto
cleanup
;
default:
default:
error
=
packfile_error
(
"invalid packfile type in header"
);
;
error
=
packfile_error
(
"invalid packfile type in header"
);
break
;
goto
cleanup
;
}
}
*
obj_offset
=
curpos
;
/*
* Finding the object we want a cached base element is
* problematic, as we need to make sure we don't accidentally
* give the caller the cached object, which it would then feel
* free to free, so we need to copy the data.
*/
if
(
cached
&&
stack_size
==
1
)
{
void
*
data
=
obj
->
data
;
obj
->
data
=
git__malloc
(
obj
->
len
+
1
);
GITERR_CHECK_ALLOC
(
obj
->
data
);
memcpy
(
obj
->
data
,
data
,
obj
->
len
+
1
);
git_atomic_dec
(
&
cached
->
refcount
);
goto
cleanup
;
}
/* we now apply each consecutive delta until we run out */
while
(
elem_pos
>
0
&&
!
error
)
{
git_rawobj
base
,
delta
;
/*
* We can now try to add the base to the cache, as
* long as it's not already the cached one.
*/
if
(
!
cached
)
free_base
=
!!
cache_add
(
&
p
->
bases
,
obj
,
elem
->
base_key
);
elem
=
&
stack
[
elem_pos
-
1
];
curpos
=
elem
->
offset
;
error
=
packfile_unpack_compressed
(
&
delta
,
p
,
&
w_curs
,
&
curpos
,
elem
->
size
,
elem
->
type
);
git_mwindow_close
(
&
w_curs
);
if
(
error
<
0
)
break
;
/* the current object becomes the new base, on which we apply the delta */
base
=
*
obj
;
obj
->
data
=
NULL
;
obj
->
len
=
0
;
obj
->
type
=
GIT_OBJ_BAD
;
error
=
git__delta_apply
(
obj
,
base
.
data
,
base
.
len
,
delta
.
data
,
delta
.
len
);
obj
->
type
=
base_type
;
/*
* We usually don't want to free the base at this
* point, as we put it into the cache in the previous
* iteration. free_base lets us know that we got the
* base object directly from the packfile, so we can free it.
*/
git__free
(
delta
.
data
);
if
(
free_base
)
{
free_base
=
0
;
git__free
(
base
.
data
);
}
if
(
cached
)
{
git_atomic_dec
(
&
cached
->
refcount
);
cached
=
NULL
;
}
if
(
error
<
0
)
break
;
elem_pos
--
;
}
cleanup:
if
(
error
<
0
)
git__free
(
obj
->
data
);
if
(
elem
)
*
obj_offset
=
elem
->
offset
;
git_array_clear
(
chain
);
return
error
;
return
error
;
}
}
...
@@ -660,7 +783,7 @@ int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p,
...
@@ -660,7 +783,7 @@ int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p,
st
=
inflateInit
(
&
obj
->
zstream
);
st
=
inflateInit
(
&
obj
->
zstream
);
if
(
st
!=
Z_OK
)
{
if
(
st
!=
Z_OK
)
{
git__free
(
obj
);
git__free
(
obj
);
giterr_set
(
GITERR_ZLIB
,
"
Failed to inflate packfile
"
);
giterr_set
(
GITERR_ZLIB
,
"
failed to init packfile stream
"
);
return
-
1
;
return
-
1
;
}
}
...
@@ -691,7 +814,7 @@ ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t
...
@@ -691,7 +814,7 @@ ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t
written
=
len
-
obj
->
zstream
.
avail_out
;
written
=
len
-
obj
->
zstream
.
avail_out
;
if
(
st
!=
Z_OK
&&
st
!=
Z_STREAM_END
)
{
if
(
st
!=
Z_OK
&&
st
!=
Z_STREAM_END
)
{
giterr_set
(
GITERR_ZLIB
,
"
Failed to inflate packfile
"
);
giterr_set
(
GITERR_ZLIB
,
"
error reading from the zlib stream
"
);
return
-
1
;
return
-
1
;
}
}
...
@@ -736,7 +859,7 @@ int packfile_unpack_compressed(
...
@@ -736,7 +859,7 @@ int packfile_unpack_compressed(
st
=
inflateInit
(
&
stream
);
st
=
inflateInit
(
&
stream
);
if
(
st
!=
Z_OK
)
{
if
(
st
!=
Z_OK
)
{
git__free
(
buffer
);
git__free
(
buffer
);
giterr_set
(
GITERR_ZLIB
,
"
Failed to inflate packfile
"
);
giterr_set
(
GITERR_ZLIB
,
"
failed to init zlib stream on unpack
"
);
return
-
1
;
return
-
1
;
}
}
...
@@ -763,7 +886,7 @@ int packfile_unpack_compressed(
...
@@ -763,7 +886,7 @@ int packfile_unpack_compressed(
if
((
st
!=
Z_STREAM_END
)
||
stream
.
total_out
!=
size
)
{
if
((
st
!=
Z_STREAM_END
)
||
stream
.
total_out
!=
size
)
{
git__free
(
buffer
);
git__free
(
buffer
);
giterr_set
(
GITERR_ZLIB
,
"
Failed to inflate packfile
"
);
giterr_set
(
GITERR_ZLIB
,
"
error inflating zlib stream
"
);
return
-
1
;
return
-
1
;
}
}
...
...
src/pack.h
View file @
bcf9792f
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
#include "mwindow.h"
#include "mwindow.h"
#include "odb.h"
#include "odb.h"
#include "oidmap.h"
#include "oidmap.h"
#include "array.h"
#define GIT_PACK_FILE_MODE 0444
#define GIT_PACK_FILE_MODE 0444
...
@@ -60,6 +61,15 @@ typedef struct git_pack_cache_entry {
...
@@ -60,6 +61,15 @@ typedef struct git_pack_cache_entry {
git_rawobj
raw
;
git_rawobj
raw
;
}
git_pack_cache_entry
;
}
git_pack_cache_entry
;
struct
pack_chain_elem
{
git_off_t
base_key
;
git_off_t
offset
;
size_t
size
;
git_otype
type
;
};
typedef
git_array_t
(
struct
pack_chain_elem
)
git_dependency_chain
;
#include "offmap.h"
#include "offmap.h"
GIT__USE_OFFMAP
;
GIT__USE_OFFMAP
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment