process delta chains in memory if max size is < 32 MB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
diff --git a/lib/delta.c b/lib/delta.c
index f8ab225..8b82432 100644
--- a/lib/delta.c
+++ b/lib/delta.c
@@ -225,6 +225,41 @@ copy_from_delta(const uint8_t **p, size_t *remain, size_t len, FILE *outfile)
return NULL;
}
+static const struct got_error *
+parse_delta_sizes(uint64_t *base_size, uint64_t *result_size,
+ const uint8_t **p, size_t *remain)
+{
+ const struct got_error *err;
+
+ /* Read the two size fields at the beginning of the stream. */
+ err = parse_size(base_size, p, remain);
+ if (err)
+ return err;
+ err = next_delta_byte(p, remain);
+ if (err)
+ return err;
+ err = parse_size(result_size, p, remain);
+ if (err)
+ return err;
+
+ return NULL;
+}
+
+const struct got_error *
+got_delta_get_sizes(uint64_t *base_size, uint64_t *result_size,
+ const uint8_t *delta_buf, size_t delta_len)
+{
+ size_t remain;
+ const uint8_t *p;
+
+ if (delta_len < GOT_DELTA_STREAM_LENGTH_MIN)
+ return got_error(GOT_ERR_BAD_DELTA);
+
+ p = delta_buf;
+ remain = delta_len;
+ return parse_delta_sizes(base_size, result_size, &p, &remain);
+}
+
const struct got_error *
got_delta_apply(FILE *base_file, const uint8_t *delta_buf,
size_t delta_len, FILE *outfile)
@@ -242,15 +277,7 @@ got_delta_apply(FILE *base_file, const uint8_t *delta_buf,
p = delta_buf;
remain = delta_len;
-
- /* Read the two size fields at the beginning of the stream. */
- err = parse_size(&base_size, &p, &remain);
- if (err)
- return err;
- err = next_delta_byte(&p, &remain);
- if (err)
- return err;
- err = parse_size(&result_size, &p, &remain);
+ err = parse_delta_sizes(&base_size, &result_size, &p, &remain);
if (err)
return err;
diff --git a/lib/got_delta_lib.h b/lib/got_delta_lib.h
index c0738c0..c92a6d0 100644
--- a/lib/got_delta_lib.h
+++ b/lib/got_delta_lib.h
@@ -34,6 +34,8 @@ struct got_delta *got_delta_open(const char *, off_t, size_t, int, size_t,
void got_delta_close(struct got_delta *);
const struct got_error *got_delta_chain_get_base_type(int *,
struct got_delta_chain *);
+const struct got_error *got_delta_get_sizes(uint64_t *, uint64_t *,
+ const uint8_t *, size_t);
const struct got_error *got_delta_apply(FILE *, const uint8_t *, size_t,
FILE *);
diff --git a/lib/pack.c b/lib/pack.c
index 1c07f63..52d0602 100644
--- a/lib/pack.c
+++ b/lib/pack.c
@@ -920,21 +920,89 @@ got_packfile_open_object(struct got_object **obj, struct got_object_id *id,
}
static const struct got_error *
+get_delta_sizes(uint64_t *base_size, uint64_t *result_size,
+ struct got_delta *delta)
+{
+ const struct got_error *err;
+ uint8_t *delta_buf = NULL;
+ size_t delta_len = 0;
+ FILE *delta_file;
+
+ delta_file = fopen(delta->path_packfile, "rb");
+ if (delta_file == NULL)
+ return got_error_from_errno();
+
+ if (fseeko(delta_file, delta->data_offset, SEEK_CUR) != 0) {
+ err = got_error_from_errno();
+ fclose(delta_file);
+ return err;
+ }
+
+ err = got_inflate_to_mem(&delta_buf, &delta_len, delta_file);
+ fclose(delta_file);
+ if (err)
+ return err;
+
+ err = got_delta_get_sizes(base_size, result_size, delta_buf, delta_len);
+ free(delta_buf);
+ return err;
+}
+
+static const struct got_error *
+get_delta_chain_max_size(uint64_t *max_size, struct got_delta_chain *deltas)
+{
+ struct got_delta *delta;
+ uint64_t base_size = 0, result_size = 0;
+
+ *max_size = 0;
+ SIMPLEQ_FOREACH(delta, &deltas->entries, entry) {
+ /* Plain object types are the delta base. */
+ if (delta->type != GOT_OBJ_TYPE_COMMIT &&
+ delta->type != GOT_OBJ_TYPE_TREE &&
+ delta->type != GOT_OBJ_TYPE_BLOB &&
+ delta->type != GOT_OBJ_TYPE_TAG) {
+ const struct got_error *err;
+ err = get_delta_sizes(&base_size, &result_size, delta);
+ if (err)
+ return err;
+ } else
+ base_size = delta->size;
+ if (base_size > *max_size)
+ *max_size = base_size;
+ if (result_size > *max_size)
+ *max_size = result_size;
+ }
+
+ return NULL;
+}
+
+static const struct got_error *
dump_delta_chain(struct got_delta_chain *deltas, FILE *outfile)
{
const struct got_error *err = NULL;
struct got_delta *delta;
- FILE *base_file, *accum_file;
+ FILE *base_file = NULL, *accum_file = NULL;
+ uint64_t max_size;
int n = 0;
if (SIMPLEQ_EMPTY(&deltas->entries))
return got_error(GOT_ERR_BAD_DELTA_CHAIN);
- base_file = got_opentemp();
+ err = get_delta_chain_max_size(&max_size, deltas);
+ if (err)
+ return err;
+
+ if (max_size < GOT_DELTA_RESULT_SIZE_CACHED_MAX)
+ base_file = fmemopen(NULL, max_size, "w+");
+ else
+ base_file = got_opentemp();
if (base_file == NULL)
return got_error_from_errno();
- accum_file = got_opentemp();
+ if (max_size < GOT_DELTA_RESULT_SIZE_CACHED_MAX)
+ accum_file = fmemopen(NULL, max_size, "w+");
+ else
+ accum_file = got_opentemp();
if (accum_file == NULL) {
err = got_error_from_errno();
fclose(base_file);
@@ -953,7 +1021,6 @@ dump_delta_chain(struct got_delta_chain *deltas, FILE *outfile)
goto done;
}
-
if (n == 0) {
/* Plain object types are the delta base. */
if (delta->type != GOT_OBJ_TYPE_COMMIT &&