Merge branch 'np/pack'
* np/pack: (27 commits) document --index-version for index-pack and pack-objects pack-objects: remove obsolete comments pack-objects: better check_object() performances add get_size_from_delta() pack-objects: make in_pack_header_size a variable of its own pack-objects: get rid of create_final_object_list() pack-objects: get rid of reuse_cached_pack pack-objects: clean up list sorting pack-objects: rework check_delta_limit usage pack-objects: equal objects in size should delta against newer objects pack-objects: optimize preferred base handling a bit clean up add_object_entry() tests for various pack index features use test-genrandom in tests instead of /dev/urandom simple random data generator for tests validate reused pack data with CRC when possible allow forcing index v2 and 64-bit offset treshold pack-redundant.c: learn about index v2 show-index.c: learn about index v2 sha1_file.c: learn about index version 2 ...
This commit is contained in:
commit
99ebd06c18
1
.gitignore
vendored
1
.gitignore
vendored
@ -149,6 +149,7 @@ test-chmtime
|
|||||||
test-date
|
test-date
|
||||||
test-delta
|
test-delta
|
||||||
test-dump-cache-tree
|
test-dump-cache-tree
|
||||||
|
test-genrandom
|
||||||
test-match-trees
|
test-match-trees
|
||||||
common-cmds.h
|
common-cmds.h
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
|
@ -68,6 +68,11 @@ OPTIONS
|
|||||||
message can later be searched for within all .keep files to
|
message can later be searched for within all .keep files to
|
||||||
locate any which have outlived their usefulness.
|
locate any which have outlived their usefulness.
|
||||||
|
|
||||||
|
--index-version=<version>[,<offset>]::
|
||||||
|
This is intended to be used by the test suite only. It allows
|
||||||
|
to force the version for the generated pack index, and to force
|
||||||
|
64-bit index entries on objects located above the given offset.
|
||||||
|
|
||||||
|
|
||||||
Note
|
Note
|
||||||
----
|
----
|
||||||
|
@ -138,6 +138,11 @@ base-name::
|
|||||||
length, this option typically shrinks the resulting
|
length, this option typically shrinks the resulting
|
||||||
packfile by 3-5 per-cent.
|
packfile by 3-5 per-cent.
|
||||||
|
|
||||||
|
--index-version=<version>[,<offset>]::
|
||||||
|
This is intended to be used by the test suite only. It allows
|
||||||
|
to force the version for the generated pack index, and to force
|
||||||
|
64-bit index entries on objects located above the given offset.
|
||||||
|
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
|
7
Makefile
7
Makefile
@ -933,7 +933,7 @@ endif
|
|||||||
|
|
||||||
export NO_SVN_TESTS
|
export NO_SVN_TESTS
|
||||||
|
|
||||||
test: all test-chmtime$X
|
test: all test-chmtime$X test-genrandom$X
|
||||||
$(MAKE) -C t/ all
|
$(MAKE) -C t/ all
|
||||||
|
|
||||||
test-date$X: test-date.c date.o ctype.o
|
test-date$X: test-date.c date.o ctype.o
|
||||||
@ -954,6 +954,9 @@ test-match-trees$X: test-match-trees.o $(GITLIBS)
|
|||||||
test-chmtime$X: test-chmtime.c
|
test-chmtime$X: test-chmtime.c
|
||||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
|
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
|
||||||
|
|
||||||
|
test-genrandom$X: test-genrandom.c
|
||||||
|
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
|
||||||
|
|
||||||
check-sha1:: test-sha1$X
|
check-sha1:: test-sha1$X
|
||||||
./test-sha1.sh
|
./test-sha1.sh
|
||||||
|
|
||||||
@ -1042,7 +1045,7 @@ dist-doc:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
|
rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
|
||||||
test-chmtime$X $(LIB_FILE) $(XDIFF_LIB)
|
test-chmtime$X test-genrandom$X $(LIB_FILE) $(XDIFF_LIB)
|
||||||
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
|
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
|
||||||
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
|
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
|
||||||
rm -rf autom4te.cache
|
rm -rf autom4te.cache
|
||||||
|
@ -111,7 +111,7 @@ int cmd_count_objects(int ac, const char **av, const char *prefix)
|
|||||||
for (p = packed_git; p; p = p->next) {
|
for (p = packed_git; p; p = p->next) {
|
||||||
if (!p->pack_local)
|
if (!p->pack_local)
|
||||||
continue;
|
continue;
|
||||||
packed += num_packed_objects(p);
|
packed += p->num_objects;
|
||||||
num_pack++;
|
num_pack++;
|
||||||
}
|
}
|
||||||
printf("count: %lu\n", loose);
|
printf("count: %lu\n", loose);
|
||||||
|
@ -661,7 +661,7 @@ int cmd_fsck(int argc, char **argv, const char *prefix)
|
|||||||
verify_pack(p, 0);
|
verify_pack(p, 0);
|
||||||
|
|
||||||
for (p = packed_git; p; p = p->next) {
|
for (p = packed_git; p; p = p->next) {
|
||||||
uint32_t i, num = num_packed_objects(p);
|
uint32_t i, num = p->num_objects;
|
||||||
for (i = 0; i < num; i++)
|
for (i = 0; i < num; i++)
|
||||||
fsck_sha1(nth_packed_object_sha1(p, i));
|
fsck_sha1(nth_packed_object_sha1(p, i));
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,8 @@ static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-fil
|
|||||||
|
|
||||||
/* We always read in 4kB chunks. */
|
/* We always read in 4kB chunks. */
|
||||||
static unsigned char buffer[4096];
|
static unsigned char buffer[4096];
|
||||||
static unsigned long offset, len, consumed_bytes;
|
static unsigned int offset, len;
|
||||||
|
static off_t consumed_bytes;
|
||||||
static SHA_CTX ctx;
|
static SHA_CTX ctx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -49,6 +50,10 @@ static void use(int bytes)
|
|||||||
die("used more bytes than were available");
|
die("used more bytes than were available");
|
||||||
len -= bytes;
|
len -= bytes;
|
||||||
offset += bytes;
|
offset += bytes;
|
||||||
|
|
||||||
|
/* make sure off_t is sufficiently large not to wrap */
|
||||||
|
if (consumed_bytes > consumed_bytes + bytes)
|
||||||
|
die("pack too large for current definition of off_t");
|
||||||
consumed_bytes += bytes;
|
consumed_bytes += bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,17 +93,17 @@ static void *get_data(unsigned long size)
|
|||||||
|
|
||||||
struct delta_info {
|
struct delta_info {
|
||||||
unsigned char base_sha1[20];
|
unsigned char base_sha1[20];
|
||||||
unsigned long base_offset;
|
unsigned nr;
|
||||||
|
off_t base_offset;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
void *delta;
|
void *delta;
|
||||||
unsigned nr;
|
|
||||||
struct delta_info *next;
|
struct delta_info *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct delta_info *delta_list;
|
static struct delta_info *delta_list;
|
||||||
|
|
||||||
static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
|
static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
|
||||||
unsigned long base_offset,
|
off_t base_offset,
|
||||||
void *delta, unsigned long size)
|
void *delta, unsigned long size)
|
||||||
{
|
{
|
||||||
struct delta_info *info = xmalloc(sizeof(*info));
|
struct delta_info *info = xmalloc(sizeof(*info));
|
||||||
@ -113,7 +118,7 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct obj_info {
|
struct obj_info {
|
||||||
unsigned long offset;
|
off_t offset;
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -200,7 +205,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
|||||||
} else {
|
} else {
|
||||||
unsigned base_found = 0;
|
unsigned base_found = 0;
|
||||||
unsigned char *pack, c;
|
unsigned char *pack, c;
|
||||||
unsigned long base_offset;
|
off_t base_offset;
|
||||||
unsigned lo, mid, hi;
|
unsigned lo, mid, hi;
|
||||||
|
|
||||||
pack = fill(1);
|
pack = fill(1);
|
||||||
@ -209,7 +214,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
|
|||||||
base_offset = c & 127;
|
base_offset = c & 127;
|
||||||
while (c & 128) {
|
while (c & 128) {
|
||||||
base_offset += 1;
|
base_offset += 1;
|
||||||
if (!base_offset || base_offset & ~(~0UL >> 7))
|
if (!base_offset || MSB(base_offset, 7))
|
||||||
die("offset value overflow for delta base object");
|
die("offset value overflow for delta base object");
|
||||||
pack = fill(1);
|
pack = fill(1);
|
||||||
c = *pack;
|
c = *pack;
|
||||||
|
9
cache.h
9
cache.h
@ -376,11 +376,12 @@ struct pack_window {
|
|||||||
extern struct packed_git {
|
extern struct packed_git {
|
||||||
struct packed_git *next;
|
struct packed_git *next;
|
||||||
struct pack_window *windows;
|
struct pack_window *windows;
|
||||||
const void *index_data;
|
|
||||||
off_t index_size;
|
|
||||||
off_t pack_size;
|
off_t pack_size;
|
||||||
time_t mtime;
|
const void *index_data;
|
||||||
|
size_t index_size;
|
||||||
|
uint32_t num_objects;
|
||||||
int index_version;
|
int index_version;
|
||||||
|
time_t mtime;
|
||||||
int pack_fd;
|
int pack_fd;
|
||||||
int pack_local;
|
int pack_local;
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
@ -431,11 +432,11 @@ extern void pack_report(void);
|
|||||||
extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
|
extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t, unsigned int *);
|
||||||
extern void unuse_pack(struct pack_window **);
|
extern void unuse_pack(struct pack_window **);
|
||||||
extern struct packed_git *add_packed_git(const char *, int, int);
|
extern struct packed_git *add_packed_git(const char *, int, int);
|
||||||
extern uint32_t num_packed_objects(const struct packed_git *p);
|
|
||||||
extern const unsigned char *nth_packed_object_sha1(const struct packed_git *, uint32_t);
|
extern const unsigned char *nth_packed_object_sha1(const struct packed_git *, uint32_t);
|
||||||
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
|
extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
|
||||||
extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
|
extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
|
||||||
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
|
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
|
||||||
|
extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
|
||||||
extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
|
extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
|
||||||
|
|
||||||
/* Dumb servers support */
|
/* Dumb servers support */
|
||||||
|
14
csum-file.c
14
csum-file.c
@ -49,6 +49,8 @@ int sha1close(struct sha1file *f, unsigned char *result, int update)
|
|||||||
|
|
||||||
int sha1write(struct sha1file *f, void *buf, unsigned int count)
|
int sha1write(struct sha1file *f, void *buf, unsigned int count)
|
||||||
{
|
{
|
||||||
|
if (f->do_crc)
|
||||||
|
f->crc32 = crc32(f->crc32, buf, count);
|
||||||
while (count) {
|
while (count) {
|
||||||
unsigned offset = f->offset;
|
unsigned offset = f->offset;
|
||||||
unsigned left = sizeof(f->buffer) - offset;
|
unsigned left = sizeof(f->buffer) - offset;
|
||||||
@ -91,6 +93,7 @@ struct sha1file *sha1create(const char *fmt, ...)
|
|||||||
f->fd = fd;
|
f->fd = fd;
|
||||||
f->error = 0;
|
f->error = 0;
|
||||||
f->offset = 0;
|
f->offset = 0;
|
||||||
|
f->do_crc = 0;
|
||||||
SHA1_Init(&f->ctx);
|
SHA1_Init(&f->ctx);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@ -111,6 +114,7 @@ struct sha1file *sha1fd(int fd, const char *name)
|
|||||||
f->fd = fd;
|
f->fd = fd;
|
||||||
f->error = 0;
|
f->error = 0;
|
||||||
f->offset = 0;
|
f->offset = 0;
|
||||||
|
f->do_crc = 0;
|
||||||
SHA1_Init(&f->ctx);
|
SHA1_Init(&f->ctx);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@ -143,4 +147,14 @@ int sha1write_compressed(struct sha1file *f, void *in, unsigned int size)
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void crc32_begin(struct sha1file *f)
|
||||||
|
{
|
||||||
|
f->crc32 = crc32(0, Z_NULL, 0);
|
||||||
|
f->do_crc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t crc32_end(struct sha1file *f)
|
||||||
|
{
|
||||||
|
f->do_crc = 0;
|
||||||
|
return f->crc32;
|
||||||
|
}
|
||||||
|
@ -7,6 +7,8 @@ struct sha1file {
|
|||||||
unsigned int offset, namelen;
|
unsigned int offset, namelen;
|
||||||
SHA_CTX ctx;
|
SHA_CTX ctx;
|
||||||
char name[PATH_MAX];
|
char name[PATH_MAX];
|
||||||
|
int do_crc;
|
||||||
|
uint32_t crc32;
|
||||||
unsigned char buffer[8192];
|
unsigned char buffer[8192];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -15,5 +17,7 @@ extern struct sha1file *sha1create(const char *fmt, ...) __attribute__((format (
|
|||||||
extern int sha1close(struct sha1file *, unsigned char *, int);
|
extern int sha1close(struct sha1file *, unsigned char *, int);
|
||||||
extern int sha1write(struct sha1file *, void *, unsigned int);
|
extern int sha1write(struct sha1file *, void *, unsigned int);
|
||||||
extern int sha1write_compressed(struct sha1file *, void *, unsigned int);
|
extern int sha1write_compressed(struct sha1file *, void *, unsigned int);
|
||||||
|
extern void crc32_begin(struct sha1file *);
|
||||||
|
extern uint32_t crc32_end(struct sha1file *);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -13,6 +13,14 @@
|
|||||||
|
|
||||||
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define TYPEOF(x) (__typeof__(x))
|
||||||
|
#else
|
||||||
|
#define TYPEOF(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits))))
|
||||||
|
|
||||||
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
|
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
|
||||||
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
|
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
|
||||||
|
108
index-pack.c
108
index-pack.c
@ -12,9 +12,10 @@ static const char index_pack_usage[] =
|
|||||||
|
|
||||||
struct object_entry
|
struct object_entry
|
||||||
{
|
{
|
||||||
unsigned long offset;
|
off_t offset;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
unsigned int hdr_size;
|
unsigned int hdr_size;
|
||||||
|
uint32_t crc32;
|
||||||
enum object_type type;
|
enum object_type type;
|
||||||
enum object_type real_type;
|
enum object_type real_type;
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
@ -22,7 +23,7 @@ struct object_entry
|
|||||||
|
|
||||||
union delta_base {
|
union delta_base {
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
unsigned long offset;
|
off_t offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -83,8 +84,10 @@ static unsigned display_progress(unsigned n, unsigned total, unsigned last_pc)
|
|||||||
|
|
||||||
/* We always read in 4kB chunks. */
|
/* We always read in 4kB chunks. */
|
||||||
static unsigned char input_buffer[4096];
|
static unsigned char input_buffer[4096];
|
||||||
static unsigned long input_offset, input_len, consumed_bytes;
|
static unsigned int input_offset, input_len;
|
||||||
|
static off_t consumed_bytes;
|
||||||
static SHA_CTX input_ctx;
|
static SHA_CTX input_ctx;
|
||||||
|
static uint32_t input_crc32;
|
||||||
static int input_fd, output_fd, pack_fd;
|
static int input_fd, output_fd, pack_fd;
|
||||||
|
|
||||||
/* Discard current buffer used content. */
|
/* Discard current buffer used content. */
|
||||||
@ -127,8 +130,13 @@ static void use(int bytes)
|
|||||||
{
|
{
|
||||||
if (bytes > input_len)
|
if (bytes > input_len)
|
||||||
die("used more bytes than were available");
|
die("used more bytes than were available");
|
||||||
|
input_crc32 = crc32(input_crc32, input_buffer + input_offset, bytes);
|
||||||
input_len -= bytes;
|
input_len -= bytes;
|
||||||
input_offset += bytes;
|
input_offset += bytes;
|
||||||
|
|
||||||
|
/* make sure off_t is sufficiently large not to wrap */
|
||||||
|
if (consumed_bytes > consumed_bytes + bytes)
|
||||||
|
die("pack too large for current definition of off_t");
|
||||||
consumed_bytes += bytes;
|
consumed_bytes += bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,10 +224,13 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size)
|
|||||||
static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
|
static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_base)
|
||||||
{
|
{
|
||||||
unsigned char *p, c;
|
unsigned char *p, c;
|
||||||
unsigned long size, base_offset;
|
unsigned long size;
|
||||||
|
off_t base_offset;
|
||||||
unsigned shift;
|
unsigned shift;
|
||||||
|
void *data;
|
||||||
|
|
||||||
obj->offset = consumed_bytes;
|
obj->offset = consumed_bytes;
|
||||||
|
input_crc32 = crc32(0, Z_NULL, 0);
|
||||||
|
|
||||||
p = fill(1);
|
p = fill(1);
|
||||||
c = *p;
|
c = *p;
|
||||||
@ -249,7 +260,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
|
|||||||
base_offset = c & 127;
|
base_offset = c & 127;
|
||||||
while (c & 128) {
|
while (c & 128) {
|
||||||
base_offset += 1;
|
base_offset += 1;
|
||||||
if (!base_offset || base_offset & ~(~0UL >> 7))
|
if (!base_offset || MSB(base_offset, 7))
|
||||||
bad_object(obj->offset, "offset value overflow for delta base object");
|
bad_object(obj->offset, "offset value overflow for delta base object");
|
||||||
p = fill(1);
|
p = fill(1);
|
||||||
c = *p;
|
c = *p;
|
||||||
@ -270,7 +281,9 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
|
|||||||
}
|
}
|
||||||
obj->hdr_size = consumed_bytes - obj->offset;
|
obj->hdr_size = consumed_bytes - obj->offset;
|
||||||
|
|
||||||
return unpack_entry_data(obj->offset, obj->size);
|
data = unpack_entry_data(obj->offset, obj->size);
|
||||||
|
obj->crc32 = input_crc32;
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *get_data_from_pack(struct object_entry *obj)
|
static void *get_data_from_pack(struct object_entry *obj)
|
||||||
@ -515,7 +528,7 @@ static void parse_pack_objects(unsigned char *sha1)
|
|||||||
fputc('\n', stderr);
|
fputc('\n', stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_compressed(int fd, void *in, unsigned int size)
|
static int write_compressed(int fd, void *in, unsigned int size, uint32_t *obj_crc)
|
||||||
{
|
{
|
||||||
z_stream stream;
|
z_stream stream;
|
||||||
unsigned long maxsize;
|
unsigned long maxsize;
|
||||||
@ -536,6 +549,7 @@ static int write_compressed(int fd, void *in, unsigned int size)
|
|||||||
|
|
||||||
size = stream.total_out;
|
size = stream.total_out;
|
||||||
write_or_die(fd, out, size);
|
write_or_die(fd, out, size);
|
||||||
|
*obj_crc = crc32(*obj_crc, out, size);
|
||||||
free(out);
|
free(out);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@ -556,8 +570,10 @@ static void append_obj_to_pack(const unsigned char *sha1, void *buf,
|
|||||||
}
|
}
|
||||||
header[n++] = c;
|
header[n++] = c;
|
||||||
write_or_die(output_fd, header, n);
|
write_or_die(output_fd, header, n);
|
||||||
|
obj[0].crc32 = crc32(0, Z_NULL, 0);
|
||||||
|
obj[0].crc32 = crc32(obj[0].crc32, header, n);
|
||||||
obj[1].offset = obj[0].offset + n;
|
obj[1].offset = obj[0].offset + n;
|
||||||
obj[1].offset += write_compressed(output_fd, buf, size);
|
obj[1].offset += write_compressed(output_fd, buf, size, &obj[0].crc32);
|
||||||
hashcpy(obj->sha1, sha1);
|
hashcpy(obj->sha1, sha1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,6 +671,9 @@ static void readjust_pack_header_and_sha1(unsigned char *sha1)
|
|||||||
write_or_die(output_fd, sha1, 20);
|
write_or_die(output_fd, sha1, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t index_default_version = 1;
|
||||||
|
static uint32_t index_off32_limit = 0x7fffffff;
|
||||||
|
|
||||||
static int sha1_compare(const void *_a, const void *_b)
|
static int sha1_compare(const void *_a, const void *_b)
|
||||||
{
|
{
|
||||||
struct object_entry *a = *(struct object_entry **)_a;
|
struct object_entry *a = *(struct object_entry **)_a;
|
||||||
@ -670,9 +689,10 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
|
|||||||
{
|
{
|
||||||
struct sha1file *f;
|
struct sha1file *f;
|
||||||
struct object_entry **sorted_by_sha, **list, **last;
|
struct object_entry **sorted_by_sha, **list, **last;
|
||||||
unsigned int array[256];
|
uint32_t array[256];
|
||||||
int i, fd;
|
int i, fd;
|
||||||
SHA_CTX ctx;
|
SHA_CTX ctx;
|
||||||
|
uint32_t index_version;
|
||||||
|
|
||||||
if (nr_objects) {
|
if (nr_objects) {
|
||||||
sorted_by_sha =
|
sorted_by_sha =
|
||||||
@ -683,7 +703,6 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
|
|||||||
sorted_by_sha[i] = &objects[i];
|
sorted_by_sha[i] = &objects[i];
|
||||||
qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
|
qsort(sorted_by_sha, nr_objects, sizeof(sorted_by_sha[0]),
|
||||||
sha1_compare);
|
sha1_compare);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sorted_by_sha = list = last = NULL;
|
sorted_by_sha = list = last = NULL;
|
||||||
@ -702,6 +721,17 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
|
|||||||
die("unable to create %s: %s", index_name, strerror(errno));
|
die("unable to create %s: %s", index_name, strerror(errno));
|
||||||
f = sha1fd(fd, index_name);
|
f = sha1fd(fd, index_name);
|
||||||
|
|
||||||
|
/* if last object's offset is >= 2^31 we should use index V2 */
|
||||||
|
index_version = (objects[nr_objects-1].offset >> 31) ? 2 : index_default_version;
|
||||||
|
|
||||||
|
/* index versions 2 and above need a header */
|
||||||
|
if (index_version >= 2) {
|
||||||
|
struct pack_idx_header hdr;
|
||||||
|
hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
|
||||||
|
hdr.idx_version = htonl(index_version);
|
||||||
|
sha1write(f, &hdr, sizeof(hdr));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write the first-level table (the list is sorted,
|
* Write the first-level table (the list is sorted,
|
||||||
* but we use a 256-entry lookup to be able to avoid
|
* but we use a 256-entry lookup to be able to avoid
|
||||||
@ -718,24 +748,61 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
|
|||||||
array[i] = htonl(next - sorted_by_sha);
|
array[i] = htonl(next - sorted_by_sha);
|
||||||
list = next;
|
list = next;
|
||||||
}
|
}
|
||||||
sha1write(f, array, 256 * sizeof(int));
|
sha1write(f, array, 256 * 4);
|
||||||
|
|
||||||
/* recompute the SHA1 hash of sorted object names.
|
/* compute the SHA1 hash of sorted object names. */
|
||||||
* currently pack-objects does not do this, but that
|
|
||||||
* can be fixed.
|
|
||||||
*/
|
|
||||||
SHA1_Init(&ctx);
|
SHA1_Init(&ctx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write the actual SHA1 entries..
|
* Write the actual SHA1 entries..
|
||||||
*/
|
*/
|
||||||
list = sorted_by_sha;
|
list = sorted_by_sha;
|
||||||
for (i = 0; i < nr_objects; i++) {
|
for (i = 0; i < nr_objects; i++) {
|
||||||
struct object_entry *obj = *list++;
|
struct object_entry *obj = *list++;
|
||||||
unsigned int offset = htonl(obj->offset);
|
if (index_version < 2) {
|
||||||
|
uint32_t offset = htonl(obj->offset);
|
||||||
sha1write(f, &offset, 4);
|
sha1write(f, &offset, 4);
|
||||||
|
}
|
||||||
sha1write(f, obj->sha1, 20);
|
sha1write(f, obj->sha1, 20);
|
||||||
SHA1_Update(&ctx, obj->sha1, 20);
|
SHA1_Update(&ctx, obj->sha1, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (index_version >= 2) {
|
||||||
|
unsigned int nr_large_offset = 0;
|
||||||
|
|
||||||
|
/* write the crc32 table */
|
||||||
|
list = sorted_by_sha;
|
||||||
|
for (i = 0; i < nr_objects; i++) {
|
||||||
|
struct object_entry *obj = *list++;
|
||||||
|
uint32_t crc32_val = htonl(obj->crc32);
|
||||||
|
sha1write(f, &crc32_val, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write the 32-bit offset table */
|
||||||
|
list = sorted_by_sha;
|
||||||
|
for (i = 0; i < nr_objects; i++) {
|
||||||
|
struct object_entry *obj = *list++;
|
||||||
|
uint32_t offset = (obj->offset <= index_off32_limit) ?
|
||||||
|
obj->offset : (0x80000000 | nr_large_offset++);
|
||||||
|
offset = htonl(offset);
|
||||||
|
sha1write(f, &offset, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write the large offset table */
|
||||||
|
list = sorted_by_sha;
|
||||||
|
while (nr_large_offset) {
|
||||||
|
struct object_entry *obj = *list++;
|
||||||
|
uint64_t offset = obj->offset;
|
||||||
|
if (offset > index_off32_limit) {
|
||||||
|
uint32_t split[2];
|
||||||
|
split[0] = htonl(offset >> 32);
|
||||||
|
split[1] = htonl(offset & 0xffffffff);
|
||||||
|
sha1write(f, split, 8);
|
||||||
|
nr_large_offset--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sha1write(f, sha1, 20);
|
sha1write(f, sha1, 20);
|
||||||
sha1close(f, NULL, 1);
|
sha1close(f, NULL, 1);
|
||||||
free(sorted_by_sha);
|
free(sorted_by_sha);
|
||||||
@ -865,6 +932,15 @@ int main(int argc, char **argv)
|
|||||||
if (index_name || (i+1) >= argc)
|
if (index_name || (i+1) >= argc)
|
||||||
usage(index_pack_usage);
|
usage(index_pack_usage);
|
||||||
index_name = argv[++i];
|
index_name = argv[++i];
|
||||||
|
} else if (!prefixcmp(arg, "--index-version=")) {
|
||||||
|
char *c;
|
||||||
|
index_default_version = strtoul(arg + 16, &c, 10);
|
||||||
|
if (index_default_version > 2)
|
||||||
|
die("bad %s", arg);
|
||||||
|
if (*c == ',')
|
||||||
|
index_off32_limit = strtoul(c+1, &c, 0);
|
||||||
|
if (*c || index_off32_limit & 0x80000000)
|
||||||
|
die("bad %s", arg);
|
||||||
} else
|
} else
|
||||||
usage(index_pack_usage);
|
usage(index_pack_usage);
|
||||||
continue;
|
continue;
|
||||||
|
@ -40,7 +40,7 @@ static int verify_packfile(struct packed_git *p,
|
|||||||
* have verified that nr_objects matches between idx and pack,
|
* have verified that nr_objects matches between idx and pack,
|
||||||
* we do not do scan-streaming check on the pack file.
|
* we do not do scan-streaming check on the pack file.
|
||||||
*/
|
*/
|
||||||
nr_objects = num_packed_objects(p);
|
nr_objects = p->num_objects;
|
||||||
for (i = 0, err = 0; i < nr_objects; i++) {
|
for (i = 0, err = 0; i < nr_objects; i++) {
|
||||||
const unsigned char *sha1;
|
const unsigned char *sha1;
|
||||||
void *data;
|
void *data;
|
||||||
@ -79,7 +79,7 @@ static void show_pack_info(struct packed_git *p)
|
|||||||
{
|
{
|
||||||
uint32_t nr_objects, i, chain_histogram[MAX_CHAIN];
|
uint32_t nr_objects, i, chain_histogram[MAX_CHAIN];
|
||||||
|
|
||||||
nr_objects = num_packed_objects(p);
|
nr_objects = p->num_objects;
|
||||||
memset(chain_histogram, 0, sizeof(chain_histogram));
|
memset(chain_histogram, 0, sizeof(chain_histogram));
|
||||||
|
|
||||||
for (i = 0; i < nr_objects; i++) {
|
for (i = 0; i < nr_objects; i++) {
|
||||||
|
@ -247,16 +247,19 @@ static struct pack_list * pack_list_difference(const struct pack_list *A,
|
|||||||
|
|
||||||
static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
|
static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
|
||||||
{
|
{
|
||||||
int p1_off, p2_off;
|
unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
|
||||||
const unsigned char *p1_base, *p2_base;
|
const unsigned char *p1_base, *p2_base;
|
||||||
struct llist_item *p1_hint = NULL, *p2_hint = NULL;
|
struct llist_item *p1_hint = NULL, *p2_hint = NULL;
|
||||||
|
|
||||||
p1_off = p2_off = 256 * 4 + 4;
|
|
||||||
p1_base = p1->pack->index_data;
|
p1_base = p1->pack->index_data;
|
||||||
p2_base = p2->pack->index_data;
|
p2_base = p2->pack->index_data;
|
||||||
|
p1_base += 256 * 4 + ((p1->pack->index_version < 2) ? 4 : 8);
|
||||||
|
p2_base += 256 * 4 + ((p2->pack->index_version < 2) ? 4 : 8);
|
||||||
|
p1_step = (p1->pack->index_version < 2) ? 24 : 20;
|
||||||
|
p2_step = (p2->pack->index_version < 2) ? 24 : 20;
|
||||||
|
|
||||||
while (p1_off <= p1->pack->index_size - 3 * 20 &&
|
while (p1_off < p1->pack->num_objects * p1_step &&
|
||||||
p2_off <= p2->pack->index_size - 3 * 20)
|
p2_off < p2->pack->num_objects * p2_step)
|
||||||
{
|
{
|
||||||
int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
|
int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
|
||||||
/* cmp ~ p1 - p2 */
|
/* cmp ~ p1 - p2 */
|
||||||
@ -265,14 +268,14 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
|
|||||||
p1_base + p1_off, p1_hint);
|
p1_base + p1_off, p1_hint);
|
||||||
p2_hint = llist_sorted_remove(p2->unique_objects,
|
p2_hint = llist_sorted_remove(p2->unique_objects,
|
||||||
p1_base + p1_off, p2_hint);
|
p1_base + p1_off, p2_hint);
|
||||||
p1_off+=24;
|
p1_off += p1_step;
|
||||||
p2_off+=24;
|
p2_off += p2_step;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (cmp < 0) { /* p1 has the object, p2 doesn't */
|
if (cmp < 0) { /* p1 has the object, p2 doesn't */
|
||||||
p1_off+=24;
|
p1_off += p1_step;
|
||||||
} else { /* p2 has the object, p1 doesn't */
|
} else { /* p2 has the object, p1 doesn't */
|
||||||
p2_off+=24;
|
p2_off += p2_step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -352,28 +355,31 @@ static int is_superset(struct pack_list *pl, struct llist *list)
|
|||||||
static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
|
static size_t sizeof_union(struct packed_git *p1, struct packed_git *p2)
|
||||||
{
|
{
|
||||||
size_t ret = 0;
|
size_t ret = 0;
|
||||||
int p1_off, p2_off;
|
unsigned long p1_off = 0, p2_off = 0, p1_step, p2_step;
|
||||||
const unsigned char *p1_base, *p2_base;
|
const unsigned char *p1_base, *p2_base;
|
||||||
|
|
||||||
p1_off = p2_off = 256 * 4 + 4;
|
|
||||||
p1_base = p1->index_data;
|
p1_base = p1->index_data;
|
||||||
p2_base = p2->index_data;
|
p2_base = p2->index_data;
|
||||||
|
p1_base += 256 * 4 + ((p1->index_version < 2) ? 4 : 8);
|
||||||
|
p2_base += 256 * 4 + ((p2->index_version < 2) ? 4 : 8);
|
||||||
|
p1_step = (p1->index_version < 2) ? 24 : 20;
|
||||||
|
p2_step = (p2->index_version < 2) ? 24 : 20;
|
||||||
|
|
||||||
while (p1_off <= p1->index_size - 3 * 20 &&
|
while (p1_off < p1->num_objects * p1_step &&
|
||||||
p2_off <= p2->index_size - 3 * 20)
|
p2_off < p2->num_objects * p2_step)
|
||||||
{
|
{
|
||||||
int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
|
int cmp = hashcmp(p1_base + p1_off, p2_base + p2_off);
|
||||||
/* cmp ~ p1 - p2 */
|
/* cmp ~ p1 - p2 */
|
||||||
if (cmp == 0) {
|
if (cmp == 0) {
|
||||||
ret++;
|
ret++;
|
||||||
p1_off+=24;
|
p1_off += p1_step;
|
||||||
p2_off+=24;
|
p2_off += p2_step;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (cmp < 0) { /* p1 has the object, p2 doesn't */
|
if (cmp < 0) { /* p1 has the object, p2 doesn't */
|
||||||
p1_off+=24;
|
p1_off += p1_step;
|
||||||
} else { /* p2 has the object, p1 doesn't */
|
} else { /* p2 has the object, p1 doesn't */
|
||||||
p2_off+=24;
|
p2_off += p2_step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@ -535,7 +541,7 @@ static void scan_alt_odb_packs(void)
|
|||||||
static struct pack_list * add_pack(struct packed_git *p)
|
static struct pack_list * add_pack(struct packed_git *p)
|
||||||
{
|
{
|
||||||
struct pack_list l;
|
struct pack_list l;
|
||||||
size_t off;
|
unsigned long off = 0, step;
|
||||||
const unsigned char *base;
|
const unsigned char *base;
|
||||||
|
|
||||||
if (!p->pack_local && !(alt_odb || verbose))
|
if (!p->pack_local && !(alt_odb || verbose))
|
||||||
@ -544,11 +550,12 @@ static struct pack_list * add_pack(struct packed_git *p)
|
|||||||
l.pack = p;
|
l.pack = p;
|
||||||
llist_init(&l.all_objects);
|
llist_init(&l.all_objects);
|
||||||
|
|
||||||
off = 256 * 4 + 4;
|
|
||||||
base = p->index_data;
|
base = p->index_data;
|
||||||
while (off <= p->index_size - 3 * 20) {
|
base += 256 * 4 + ((p->index_version < 2) ? 4 : 8);
|
||||||
|
step = (p->index_version < 2) ? 24 : 20;
|
||||||
|
while (off < p->num_objects * step) {
|
||||||
llist_insert_back(l.all_objects, base + off);
|
llist_insert_back(l.all_objects, base + off);
|
||||||
off += 24;
|
off += step;
|
||||||
}
|
}
|
||||||
/* this list will be pruned in cmp_two_packs later */
|
/* this list will be pruned in cmp_two_packs later */
|
||||||
l.unique_objects = llist_copy(l.all_objects);
|
l.unique_objects = llist_copy(l.all_objects);
|
||||||
|
178
sha1_file.c
178
sha1_file.c
@ -437,7 +437,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
|||||||
void *idx_map;
|
void *idx_map;
|
||||||
struct pack_idx_header *hdr;
|
struct pack_idx_header *hdr;
|
||||||
size_t idx_size;
|
size_t idx_size;
|
||||||
uint32_t nr, i, *index;
|
uint32_t version, nr, i, *index;
|
||||||
int fd = open(path, O_RDONLY);
|
int fd = open(path, O_RDONLY);
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
@ -455,21 +455,23 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
|||||||
idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
/* a future index format would start with this, as older git
|
|
||||||
* binaries would fail the non-monotonic index check below.
|
|
||||||
* give a nicer warning to the user if we can.
|
|
||||||
*/
|
|
||||||
hdr = idx_map;
|
hdr = idx_map;
|
||||||
if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
|
if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
|
||||||
|
version = ntohl(hdr->idx_version);
|
||||||
|
if (version < 2 || version > 2) {
|
||||||
munmap(idx_map, idx_size);
|
munmap(idx_map, idx_size);
|
||||||
return error("index file %s is a newer version"
|
return error("index file %s is version %d"
|
||||||
" and is not supported by this binary"
|
" and is not supported by this binary"
|
||||||
" (try upgrading GIT to a newer version)",
|
" (try upgrading GIT to a newer version)",
|
||||||
path);
|
path, version);
|
||||||
}
|
}
|
||||||
|
} else
|
||||||
|
version = 1;
|
||||||
|
|
||||||
nr = 0;
|
nr = 0;
|
||||||
index = idx_map;
|
index = idx_map;
|
||||||
|
if (version > 1)
|
||||||
|
index += 2; /* skip index header */
|
||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
uint32_t n = ntohl(index[i]);
|
uint32_t n = ntohl(index[i]);
|
||||||
if (n < nr) {
|
if (n < nr) {
|
||||||
@ -479,6 +481,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
|||||||
nr = n;
|
nr = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version == 1) {
|
||||||
/*
|
/*
|
||||||
* Total size:
|
* Total size:
|
||||||
* - 256 index entries 4 bytes each
|
* - 256 index entries 4 bytes each
|
||||||
@ -490,10 +493,39 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
|
|||||||
munmap(idx_map, idx_size);
|
munmap(idx_map, idx_size);
|
||||||
return error("wrong index file size in %s", path);
|
return error("wrong index file size in %s", path);
|
||||||
}
|
}
|
||||||
|
} else if (version == 2) {
|
||||||
|
/*
|
||||||
|
* Minimum size:
|
||||||
|
* - 8 bytes of header
|
||||||
|
* - 256 index entries 4 bytes each
|
||||||
|
* - 20-byte sha1 entry * nr
|
||||||
|
* - 4-byte crc entry * nr
|
||||||
|
* - 4-byte offset entry * nr
|
||||||
|
* - 20-byte SHA1 of the packfile
|
||||||
|
* - 20-byte SHA1 file checksum
|
||||||
|
* And after the 4-byte offset table might be a
|
||||||
|
* variable sized table containing 8-byte entries
|
||||||
|
* for offsets larger than 2^31.
|
||||||
|
*/
|
||||||
|
unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
|
||||||
|
if (idx_size < min_size || idx_size > min_size + (nr - 1)*8) {
|
||||||
|
munmap(idx_map, idx_size);
|
||||||
|
return error("wrong index file size in %s", path);
|
||||||
|
}
|
||||||
|
if (idx_size != min_size) {
|
||||||
|
/* make sure we can deal with large pack offsets */
|
||||||
|
off_t x = 0x7fffffffUL, y = 0xffffffffUL;
|
||||||
|
if (x > (x + 1) || y > (y + 1)) {
|
||||||
|
munmap(idx_map, idx_size);
|
||||||
|
return error("pack too large for current definition of off_t in %s", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p->index_version = 1;
|
p->index_version = version;
|
||||||
p->index_data = idx_map;
|
p->index_data = idx_map;
|
||||||
p->index_size = idx_size;
|
p->index_size = idx_size;
|
||||||
|
p->num_objects = nr;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,11 +637,11 @@ static int open_packed_git_1(struct packed_git *p)
|
|||||||
p->pack_name, ntohl(hdr.hdr_version));
|
p->pack_name, ntohl(hdr.hdr_version));
|
||||||
|
|
||||||
/* Verify the pack matches its index. */
|
/* Verify the pack matches its index. */
|
||||||
if (num_packed_objects(p) != ntohl(hdr.hdr_entries))
|
if (p->num_objects != ntohl(hdr.hdr_entries))
|
||||||
return error("packfile %s claims to have %u objects"
|
return error("packfile %s claims to have %u objects"
|
||||||
" while index size indicates %u objects",
|
" while index indicates %u objects",
|
||||||
p->pack_name, ntohl(hdr.hdr_entries),
|
p->pack_name, ntohl(hdr.hdr_entries),
|
||||||
num_packed_objects(p));
|
p->num_objects);
|
||||||
if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
|
if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
|
||||||
return error("end of packfile %s is unavailable", p->pack_name);
|
return error("end of packfile %s is unavailable", p->pack_name);
|
||||||
if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
|
if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
|
||||||
@ -1128,6 +1160,43 @@ static void *unpack_sha1_file(void *map, unsigned long mapsize, enum object_type
|
|||||||
return unpack_sha1_rest(&stream, hdr, *size, sha1);
|
return unpack_sha1_rest(&stream, hdr, *size, sha1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long get_size_from_delta(struct packed_git *p,
|
||||||
|
struct pack_window **w_curs,
|
||||||
|
off_t curpos)
|
||||||
|
{
|
||||||
|
const unsigned char *data;
|
||||||
|
unsigned char delta_head[20], *in;
|
||||||
|
z_stream stream;
|
||||||
|
int st;
|
||||||
|
|
||||||
|
memset(&stream, 0, sizeof(stream));
|
||||||
|
stream.next_out = delta_head;
|
||||||
|
stream.avail_out = sizeof(delta_head);
|
||||||
|
|
||||||
|
inflateInit(&stream);
|
||||||
|
do {
|
||||||
|
in = use_pack(p, w_curs, curpos, &stream.avail_in);
|
||||||
|
stream.next_in = in;
|
||||||
|
st = inflate(&stream, Z_FINISH);
|
||||||
|
curpos += stream.next_in - in;
|
||||||
|
} while ((st == Z_OK || st == Z_BUF_ERROR) &&
|
||||||
|
stream.total_out < sizeof(delta_head));
|
||||||
|
inflateEnd(&stream);
|
||||||
|
if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
|
||||||
|
die("delta data unpack-initial failed");
|
||||||
|
|
||||||
|
/* Examine the initial part of the delta to figure out
|
||||||
|
* the result size.
|
||||||
|
*/
|
||||||
|
data = delta_head;
|
||||||
|
|
||||||
|
/* ignore base size */
|
||||||
|
get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
|
||||||
|
|
||||||
|
/* Read the result size */
|
||||||
|
return get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
|
||||||
|
}
|
||||||
|
|
||||||
static off_t get_delta_base(struct packed_git *p,
|
static off_t get_delta_base(struct packed_git *p,
|
||||||
struct pack_window **w_curs,
|
struct pack_window **w_curs,
|
||||||
off_t *curpos,
|
off_t *curpos,
|
||||||
@ -1149,7 +1218,7 @@ static off_t get_delta_base(struct packed_git *p,
|
|||||||
base_offset = c & 127;
|
base_offset = c & 127;
|
||||||
while (c & 128) {
|
while (c & 128) {
|
||||||
base_offset += 1;
|
base_offset += 1;
|
||||||
if (!base_offset || base_offset & ~(~0UL >> 7))
|
if (!base_offset || MSB(base_offset, 7))
|
||||||
die("offset value overflow for delta base object");
|
die("offset value overflow for delta base object");
|
||||||
c = base_info[used++];
|
c = base_info[used++];
|
||||||
base_offset = (base_offset << 7) + (c & 127);
|
base_offset = (base_offset << 7) + (c & 127);
|
||||||
@ -1191,40 +1260,8 @@ static int packed_delta_info(struct packed_git *p,
|
|||||||
* based on a base with a wrong size. This saves tons of
|
* based on a base with a wrong size. This saves tons of
|
||||||
* inflate() calls.
|
* inflate() calls.
|
||||||
*/
|
*/
|
||||||
if (sizep) {
|
if (sizep)
|
||||||
const unsigned char *data;
|
*sizep = get_size_from_delta(p, w_curs, curpos);
|
||||||
unsigned char delta_head[20], *in;
|
|
||||||
z_stream stream;
|
|
||||||
int st;
|
|
||||||
|
|
||||||
memset(&stream, 0, sizeof(stream));
|
|
||||||
stream.next_out = delta_head;
|
|
||||||
stream.avail_out = sizeof(delta_head);
|
|
||||||
|
|
||||||
inflateInit(&stream);
|
|
||||||
do {
|
|
||||||
in = use_pack(p, w_curs, curpos, &stream.avail_in);
|
|
||||||
stream.next_in = in;
|
|
||||||
st = inflate(&stream, Z_FINISH);
|
|
||||||
curpos += stream.next_in - in;
|
|
||||||
} while ((st == Z_OK || st == Z_BUF_ERROR)
|
|
||||||
&& stream.total_out < sizeof(delta_head));
|
|
||||||
inflateEnd(&stream);
|
|
||||||
if ((st != Z_STREAM_END) &&
|
|
||||||
stream.total_out != sizeof(delta_head))
|
|
||||||
die("delta data unpack-initial failed");
|
|
||||||
|
|
||||||
/* Examine the initial part of the delta to figure out
|
|
||||||
* the result size.
|
|
||||||
*/
|
|
||||||
data = delta_head;
|
|
||||||
|
|
||||||
/* ignore base size */
|
|
||||||
get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
|
|
||||||
|
|
||||||
/* Read the result size */
|
|
||||||
*sizep = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
|
|
||||||
}
|
|
||||||
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@ -1526,37 +1563,60 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t num_packed_objects(const struct packed_git *p)
|
|
||||||
{
|
|
||||||
/* See check_packed_git_idx() */
|
|
||||||
return (uint32_t)((p->index_size - 20 - 20 - 4*256) / 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
|
const unsigned char *nth_packed_object_sha1(const struct packed_git *p,
|
||||||
uint32_t n)
|
uint32_t n)
|
||||||
{
|
{
|
||||||
const unsigned char *index = p->index_data;
|
const unsigned char *index = p->index_data;
|
||||||
index += 4 * 256;
|
if (n >= p->num_objects)
|
||||||
if (num_packed_objects(p) <= n)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
index += 4 * 256;
|
||||||
|
if (p->index_version == 1) {
|
||||||
return index + 24 * n + 4;
|
return index + 24 * n + 4;
|
||||||
|
} else {
|
||||||
|
index += 8;
|
||||||
|
return index + 20 * n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static off_t nth_packed_object_offset(const struct packed_git *p, uint32_t n)
|
||||||
|
{
|
||||||
|
const unsigned char *index = p->index_data;
|
||||||
|
index += 4 * 256;
|
||||||
|
if (p->index_version == 1) {
|
||||||
|
return ntohl(*((uint32_t *)(index + 24 * n)));
|
||||||
|
} else {
|
||||||
|
uint32_t off;
|
||||||
|
index += 8 + p->num_objects * (20 + 4);
|
||||||
|
off = ntohl(*((uint32_t *)(index + 4 * n)));
|
||||||
|
if (!(off & 0x80000000))
|
||||||
|
return off;
|
||||||
|
index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
|
||||||
|
return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
|
||||||
|
ntohl(*((uint32_t *)(index + 4)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
off_t find_pack_entry_one(const unsigned char *sha1,
|
off_t find_pack_entry_one(const unsigned char *sha1,
|
||||||
struct packed_git *p)
|
struct packed_git *p)
|
||||||
{
|
{
|
||||||
const uint32_t *level1_ofs = p->index_data;
|
const uint32_t *level1_ofs = p->index_data;
|
||||||
int hi = ntohl(level1_ofs[*sha1]);
|
|
||||||
int lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
|
|
||||||
const unsigned char *index = p->index_data;
|
const unsigned char *index = p->index_data;
|
||||||
|
unsigned hi, lo;
|
||||||
|
|
||||||
|
if (p->index_version > 1) {
|
||||||
|
level1_ofs += 2;
|
||||||
|
index += 8;
|
||||||
|
}
|
||||||
index += 4 * 256;
|
index += 4 * 256;
|
||||||
|
hi = ntohl(level1_ofs[*sha1]);
|
||||||
|
lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int mi = (lo + hi) / 2;
|
unsigned mi = (lo + hi) / 2;
|
||||||
int cmp = hashcmp(index + 24 * mi + 4, sha1);
|
unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4);
|
||||||
|
int cmp = hashcmp(index + x, sha1);
|
||||||
if (!cmp)
|
if (!cmp)
|
||||||
return ntohl(*((uint32_t *)((char *)index + (24 * mi))));
|
return nth_packed_object_offset(p, mi);
|
||||||
if (cmp > 0)
|
if (cmp > 0)
|
||||||
hi = mi;
|
hi = mi;
|
||||||
else
|
else
|
||||||
|
@ -76,7 +76,7 @@ static int find_short_packed_object(int len, const unsigned char *match, unsigne
|
|||||||
|
|
||||||
prepare_packed_git();
|
prepare_packed_git();
|
||||||
for (p = packed_git; p && found < 2; p = p->next) {
|
for (p = packed_git; p && found < 2; p = p->next) {
|
||||||
uint32_t num = num_packed_objects(p);
|
uint32_t num = p->num_objects;
|
||||||
uint32_t first = 0, last = num;
|
uint32_t first = 0, last = num;
|
||||||
while (first < last) {
|
while (first < last) {
|
||||||
uint32_t mid = (first + last) / 2;
|
uint32_t mid = (first + last) / 2;
|
||||||
|
58
show-index.c
58
show-index.c
@ -1,14 +1,26 @@
|
|||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "pack.h"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
unsigned nr;
|
unsigned nr;
|
||||||
unsigned int entry[6];
|
unsigned int version;
|
||||||
static unsigned int top_index[256];
|
static unsigned int top_index[256];
|
||||||
|
|
||||||
if (fread(top_index, sizeof(top_index), 1, stdin) != 1)
|
if (fread(top_index, 2 * 4, 1, stdin) != 1)
|
||||||
|
die("unable to read header");
|
||||||
|
if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
|
||||||
|
version = ntohl(top_index[1]);
|
||||||
|
if (version < 2 || version > 2)
|
||||||
|
die("unknown index version");
|
||||||
|
if (fread(top_index, 256 * 4, 1, stdin) != 1)
|
||||||
die("unable to read index");
|
die("unable to read index");
|
||||||
|
} else {
|
||||||
|
version = 1;
|
||||||
|
if (fread(&top_index[2], 254 * 4, 1, stdin) != 1)
|
||||||
|
die("unable to read index");
|
||||||
|
}
|
||||||
nr = 0;
|
nr = 0;
|
||||||
for (i = 0; i < 256; i++) {
|
for (i = 0; i < 256; i++) {
|
||||||
unsigned n = ntohl(top_index[i]);
|
unsigned n = ntohl(top_index[i]);
|
||||||
@ -16,13 +28,51 @@ int main(int argc, char **argv)
|
|||||||
die("corrupt index file");
|
die("corrupt index file");
|
||||||
nr = n;
|
nr = n;
|
||||||
}
|
}
|
||||||
|
if (version == 1) {
|
||||||
for (i = 0; i < nr; i++) {
|
for (i = 0; i < nr; i++) {
|
||||||
unsigned offset;
|
unsigned int offset, entry[6];
|
||||||
|
|
||||||
if (fread(entry, 24, 1, stdin) != 1)
|
if (fread(entry, 4 + 20, 1, stdin) != 1)
|
||||||
die("unable to read entry %u/%u", i, nr);
|
die("unable to read entry %u/%u", i, nr);
|
||||||
offset = ntohl(entry[0]);
|
offset = ntohl(entry[0]);
|
||||||
printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
|
printf("%u %s\n", offset, sha1_to_hex((void *)(entry+1)));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
unsigned off64_nr = 0;
|
||||||
|
struct {
|
||||||
|
unsigned char sha1[20];
|
||||||
|
uint32_t crc;
|
||||||
|
uint32_t off;
|
||||||
|
} *entries = xmalloc(nr * sizeof(entries[0]));
|
||||||
|
for (i = 0; i < nr; i++)
|
||||||
|
if (fread(entries[i].sha1, 20, 1, stdin) != 1)
|
||||||
|
die("unable to read sha1 %u/%u", i, nr);
|
||||||
|
for (i = 0; i < nr; i++)
|
||||||
|
if (fread(&entries[i].crc, 4, 1, stdin) != 1)
|
||||||
|
die("unable to read crc %u/%u", i, nr);
|
||||||
|
for (i = 0; i < nr; i++)
|
||||||
|
if (fread(&entries[i].off, 4, 1, stdin) != 1)
|
||||||
|
die("unable to read 32b offset %u/%u", i, nr);
|
||||||
|
for (i = 0; i < nr; i++) {
|
||||||
|
uint64_t offset;
|
||||||
|
uint32_t off = ntohl(entries[i].off);
|
||||||
|
if (!(off & 0x80000000)) {
|
||||||
|
offset = off;
|
||||||
|
} else {
|
||||||
|
uint32_t off64[2];
|
||||||
|
if ((off & 0x7fffffff) != off64_nr)
|
||||||
|
die("inconsistent 64b offset index");
|
||||||
|
if (fread(off64, 8, 1, stdin) != 1)
|
||||||
|
die("unable to read 64b offset %u", off64_nr);
|
||||||
|
offset = (((uint64_t)ntohl(off64[0])) << 32) |
|
||||||
|
ntohl(off64[1]);
|
||||||
|
off64_nr++;
|
||||||
|
}
|
||||||
|
printf("%llu %s (%08x)\n", (unsigned long long) offset,
|
||||||
|
sha1_to_hex(entries[i].sha1),
|
||||||
|
ntohl(entries[i].crc));
|
||||||
|
}
|
||||||
|
free(entries);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ test_expect_success \
|
|||||||
for i in a b c
|
for i in a b c
|
||||||
do
|
do
|
||||||
echo $i >$i &&
|
echo $i >$i &&
|
||||||
dd if=/dev/urandom bs=32k count=1 >>$i &&
|
test-genrandom "$i" 32768 >>$i &&
|
||||||
git-update-index --add $i || return 1
|
git-update-index --add $i || return 1
|
||||||
done &&
|
done &&
|
||||||
echo d >d && cat c >>d && git-update-index --add d &&
|
echo d >d && cat c >>d && git-update-index --add d &&
|
||||||
|
146
t/t5302-pack-index.sh
Executable file
146
t/t5302-pack-index.sh
Executable file
@ -0,0 +1,146 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Copyright (c) 2007 Nicolas Pitre
|
||||||
|
#
|
||||||
|
|
||||||
|
test_description='pack index with 64-bit offsets and object CRC'
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'setup' \
|
||||||
|
'rm -rf .git
|
||||||
|
git-init &&
|
||||||
|
for i in `seq -w 100`
|
||||||
|
do
|
||||||
|
echo $i >file_$i &&
|
||||||
|
test-genrandom "$i" 8192 >>file_$i &&
|
||||||
|
git-update-index --add file_$i || return 1
|
||||||
|
done &&
|
||||||
|
echo 101 >file_101 && tail -c 8192 file_100 >>file_101 &&
|
||||||
|
git-update-index --add file_101 &&
|
||||||
|
tree=`git-write-tree` &&
|
||||||
|
commit=`git-commit-tree $tree </dev/null` && {
|
||||||
|
echo $tree &&
|
||||||
|
git-ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
|
||||||
|
} >obj-list &&
|
||||||
|
git-update-ref HEAD $commit'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'pack-objects with index version 1' \
|
||||||
|
'pack1=$(git-pack-objects --index-version=1 test-1 <obj-list) &&
|
||||||
|
git-verify-pack -v "test-1-${pack1}.pack"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'pack-objects with index version 2' \
|
||||||
|
'pack2=$(git-pack-objects --index-version=2 test-2 <obj-list) &&
|
||||||
|
git-verify-pack -v "test-2-${pack2}.pack"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'both packs should be identical' \
|
||||||
|
'cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'index v1 and index v2 should be different' \
|
||||||
|
'cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'index-pack with index version 1' \
|
||||||
|
'git-index-pack --index-version=1 -o 1.idx "test-1-${pack1}.pack"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'index-pack with index version 2' \
|
||||||
|
'git-index-pack --index-version=2 -o 2.idx "test-1-${pack1}.pack"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'index-pack results should match pack-objects ones' \
|
||||||
|
'cmp "test-1-${pack1}.idx" "1.idx" &&
|
||||||
|
cmp "test-2-${pack2}.idx" "2.idx"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'index v2: force some 64-bit offsets with pack-objects' \
|
||||||
|
'pack3=$(git-pack-objects --index-version=2,0x40000 test-3 <obj-list) &&
|
||||||
|
git-verify-pack -v "test-3-${pack3}.pack"'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'64-bit offsets: should be different from previous index v2 results' \
|
||||||
|
'cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'index v2: force some 64-bit offsets with index-pack' \
|
||||||
|
'git-index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'64-bit offsets: index-pack result should match pack-objects one' \
|
||||||
|
'cmp "test-3-${pack3}.idx" "3.idx"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'[index v1] 1) stream pack to repository' \
|
||||||
|
'git-index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
|
||||||
|
git-prune-packed &&
|
||||||
|
git-count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
|
||||||
|
cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
|
||||||
|
cmp "test-1-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'[index v1] 2) create a stealth corruption in a delta base reference' \
|
||||||
|
'# this test assumes a delta smaller than 16 bytes at the end of the pack
|
||||||
|
git-show-index <1.idx | sort -n | tail -n 1 | (
|
||||||
|
read delta_offs delta_sha1 &&
|
||||||
|
git-cat-file blob "$delta_sha1" > blob_1 &&
|
||||||
|
chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
|
||||||
|
dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
|
||||||
|
if=".git/objects/pack/pack-${pack1}.idx" skip=$((256 * 4 + 4)) \
|
||||||
|
bs=1 count=20 conv=notrunc &&
|
||||||
|
git-cat-file blob "$delta_sha1" > blob_2 )'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'[index v1] 3) corrupted delta happily returned wrong data' \
|
||||||
|
'cmp blob_1 blob_2'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'[index v1] 4) confirm that the pack is actually corrupted' \
|
||||||
|
'git-fsck --full $commit'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'[index v1] 5) pack-objects happily reuses corrupted data' \
|
||||||
|
'pack4=$(git-pack-objects test-4 <obj-list) &&
|
||||||
|
test -f "test-4-${pack1}.pack"'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'[index v1] 6) newly created pack is BAD !' \
|
||||||
|
'git-verify-pack -v "test-4-${pack1}.pack"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'[index v2] 1) stream pack to repository' \
|
||||||
|
'rm -f .git/objects/pack/* &&
|
||||||
|
git-index-pack --index-version=2,0x40000 --stdin < "test-1-${pack1}.pack" &&
|
||||||
|
git-prune-packed &&
|
||||||
|
git-count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
|
||||||
|
cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
|
||||||
|
cmp "test-3-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
|
||||||
|
|
||||||
|
test_expect_success \
|
||||||
|
'[index v2] 2) create a stealth corruption in a delta base reference' \
|
||||||
|
'# this test assumes a delta smaller than 16 bytes at the end of the pack
|
||||||
|
git-show-index <1.idx | sort -n | tail -n 1 | (
|
||||||
|
read delta_offs delta_sha1 delta_crc &&
|
||||||
|
git-cat-file blob "$delta_sha1" > blob_3 &&
|
||||||
|
chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
|
||||||
|
dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
|
||||||
|
if=".git/objects/pack/pack-${pack1}.idx" skip=$((8 + 256 * 4)) \
|
||||||
|
bs=1 count=20 conv=notrunc &&
|
||||||
|
git-cat-file blob "$delta_sha1" > blob_4 )'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'[index v2] 3) corrupted delta happily returned wrong data' \
|
||||||
|
'cmp blob_3 blob_4'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'[index v2] 4) confirm that the pack is actually corrupted' \
|
||||||
|
'git-fsck --full $commit'
|
||||||
|
|
||||||
|
test_expect_failure \
|
||||||
|
'[index v2] 5) pack-objects refuses to reuse corrupted data' \
|
||||||
|
'git-pack-objects test-5 <obj-list'
|
||||||
|
|
||||||
|
test_done
|
34
test-genrandom.c
Normal file
34
test-genrandom.c
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Simple random data generator used to create reproducible test files.
|
||||||
|
* This is inspired from POSIX.1-2001 implementation example for rand().
|
||||||
|
* Copyright (C) 2007 by Nicolas Pitre, licensed under the GPL version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
unsigned long count, next = 0;
|
||||||
|
unsigned char *c;
|
||||||
|
|
||||||
|
if (argc < 2 || argc > 3) {
|
||||||
|
fprintf( stderr, "Usage: %s <seed_string> [<size>]", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = (unsigned char *) argv[1];
|
||||||
|
do {
|
||||||
|
next = next * 11 + *c;
|
||||||
|
} while (*c++);
|
||||||
|
|
||||||
|
count = (argc == 3) ? strtoul(argv[2], NULL, 0) : -1L;
|
||||||
|
|
||||||
|
while (count--) {
|
||||||
|
next = next * 1103515245 + 12345;
|
||||||
|
if (putchar((next >> 16) & 0xff) == EOF)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user