Last stop in the delta-encoding API widening for >4 GiB blobs on
Windows: with `create_delta_index()` done in the prior commit and
`create_delta()`/`diff_delta()` finished here, every byte count that
crosses delta.h is now `size_t`. The struct fields they store into have
been `size_t` since the diff-delta struct widening.
The API change must move with all callers in the same commit (the build
only passes when every `&delta_size` matches the new `size_t*`). Caller
updates are kept minimal:
* builtin/pack-objects.c `get_delta()` and `try_delta()`: widen only
the local `delta_size` variable; the surrounding unsigned-long
locals and their `cast_size_t_to_ulong()` shims are out of scope
here and will be cleaned up in their own commits.
* builtin/fast-import.c, diff.c, t/helper/test-pack-deltas.c:
keep the local unsigned-long delta size (each feeds a still-
unsigned-long downstream consumer: zlib's `avail_in`,
`deflate_it()`, the test helper's own `do_compress()`), and bridge
via a temporary `size_t` plus `cast_size_t_to_ulong()`. The new
casts are paid back in later topics that widen those consumers.
* t/helper/test-delta.c: widen the local outright (no downstream
consumer beyond the test's own `out_size`, which is already
`size_t`).
Note that GCC struggles a bit to figure out that `deltalen` is always
initialized before it is used; To help it along, we initialize it to 0.
This work-around will go away in a later patch series when `deltalen`
can be widened to `size_t`.
Assisted-by: Opus 4.7
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
149 lines
3.8 KiB
C
149 lines
3.8 KiB
C
#define USE_THE_REPOSITORY_VARIABLE
|
|
|
|
#include "test-tool.h"
|
|
#include "git-compat-util.h"
|
|
#include "delta.h"
|
|
#include "git-zlib.h"
|
|
#include "hash.h"
|
|
#include "hex.h"
|
|
#include "pack.h"
|
|
#include "pack-objects.h"
|
|
#include "parse-options.h"
|
|
#include "setup.h"
|
|
#include "strbuf.h"
|
|
#include "string-list.h"
|
|
|
|
static const char *usage_str[] = {
|
|
"test-tool pack-deltas --num-objects <num-objects>",
|
|
NULL
|
|
};
|
|
|
|
static unsigned long do_compress(void **pptr, unsigned long size)
|
|
{
|
|
git_zstream stream;
|
|
void *in, *out;
|
|
unsigned long maxsize;
|
|
|
|
git_deflate_init(&stream, 1);
|
|
maxsize = git_deflate_bound(&stream, size);
|
|
|
|
in = *pptr;
|
|
out = xmalloc(maxsize);
|
|
*pptr = out;
|
|
|
|
stream.next_in = in;
|
|
stream.avail_in = size;
|
|
stream.next_out = out;
|
|
stream.avail_out = maxsize;
|
|
while (git_deflate(&stream, Z_FINISH) == Z_OK)
|
|
; /* nothing */
|
|
git_deflate_end(&stream);
|
|
|
|
free(in);
|
|
return stream.total_out;
|
|
}
|
|
|
|
static void write_ref_delta(struct hashfile *f,
|
|
struct object_id *oid,
|
|
struct object_id *base)
|
|
{
|
|
unsigned char header[MAX_PACK_OBJECT_HEADER];
|
|
unsigned long delta_size, compressed_size, hdrlen;
|
|
size_t size, base_size, delta_size_st = 0;
|
|
enum object_type type;
|
|
void *base_buf, *delta_buf;
|
|
void *buf = odb_read_object(the_repository->objects,
|
|
oid, &type, &size);
|
|
|
|
if (!buf)
|
|
die("unable to read %s", oid_to_hex(oid));
|
|
|
|
base_buf = odb_read_object(the_repository->objects,
|
|
base, &type, &base_size);
|
|
|
|
if (!base_buf)
|
|
die("unable to read %s", oid_to_hex(base));
|
|
|
|
delta_buf = diff_delta(base_buf, base_size,
|
|
buf, size, &delta_size_st, 0);
|
|
delta_size = cast_size_t_to_ulong(delta_size_st);
|
|
|
|
compressed_size = do_compress(&delta_buf, delta_size);
|
|
|
|
hdrlen = encode_in_pack_object_header(header, sizeof(header),
|
|
OBJ_REF_DELTA, delta_size);
|
|
hashwrite(f, header, hdrlen);
|
|
hashwrite(f, base->hash, the_repository->hash_algo->rawsz);
|
|
hashwrite(f, delta_buf, compressed_size);
|
|
|
|
free(buf);
|
|
free(base_buf);
|
|
free(delta_buf);
|
|
}
|
|
|
|
int cmd__pack_deltas(int argc, const char **argv)
|
|
{
|
|
int num_objects = -1;
|
|
struct hashfile *f;
|
|
struct strbuf line = STRBUF_INIT;
|
|
struct option options[] = {
|
|
OPT_INTEGER('n', "num-objects", &num_objects, N_("the number of objects to write")),
|
|
OPT_END()
|
|
};
|
|
|
|
argc = parse_options(argc, argv, NULL,
|
|
options, usage_str, 0);
|
|
|
|
if (argc || num_objects < 0)
|
|
usage_with_options(usage_str, options);
|
|
|
|
setup_git_directory(the_repository);
|
|
|
|
f = hashfd(the_repository->hash_algo, 1, "<stdout>");
|
|
write_pack_header(f, num_objects);
|
|
|
|
/* Read each line from stdin into 'line' */
|
|
while (strbuf_getline_lf(&line, stdin) != EOF) {
|
|
const char *type_str, *content_oid_str, *base_oid_str = NULL;
|
|
struct object_id content_oid, base_oid;
|
|
struct string_list items = STRING_LIST_INIT_NODUP;
|
|
/*
|
|
* Tokenize into two or three parts:
|
|
* 1. REF_DELTA, OFS_DELTA, or FULL.
|
|
* 2. The object ID for the content object.
|
|
* 3. The object ID for the base object (optional).
|
|
*/
|
|
if (string_list_split_in_place(&items, line.buf, " ", 3) < 0)
|
|
die("invalid input format: %s", line.buf);
|
|
|
|
if (items.nr < 2)
|
|
die("invalid input format: %s", line.buf);
|
|
|
|
type_str = items.items[0].string;
|
|
content_oid_str = items.items[1].string;
|
|
|
|
if (get_oid_hex(content_oid_str, &content_oid))
|
|
die("invalid object: %s", content_oid_str);
|
|
if (items.nr >= 3) {
|
|
base_oid_str = items.items[2].string;
|
|
if (get_oid_hex(base_oid_str, &base_oid))
|
|
die("invalid object: %s", base_oid_str);
|
|
}
|
|
string_list_clear(&items, 0);
|
|
|
|
if (!strcmp(type_str, "REF_DELTA"))
|
|
write_ref_delta(f, &content_oid, &base_oid);
|
|
else if (!strcmp(type_str, "OFS_DELTA"))
|
|
die("OFS_DELTA not implemented");
|
|
else if (!strcmp(type_str, "FULL"))
|
|
die("FULL not implemented");
|
|
else
|
|
die("unknown pack type: %s", type_str);
|
|
}
|
|
|
|
finalize_hashfile(f, NULL, FSYNC_COMPONENT_PACK,
|
|
CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);
|
|
strbuf_release(&line);
|
|
return 0;
|
|
}
|