index-pack: eliminate unlimited recursion in get_base_data()

Revese the order of delta applying so that by the time a delta is applied, its base is either non-delta or already inflated. get_base_data() is still recursive, but because base's data is always ready, the inner get_base_data() call never has any chance to call itself again. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Jan 14, 2012 at 19:19 UTC 20e95d0a8475ce467903e1aaad5c2fd47777fcc8
1 file changed +44 -9
builtin/index-pack.c
+44 -9
@@ -515,14 +515,52 @@ static int is_delta_type(enum object_type type)
515 return (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA);
516 }
517
518 +/*
519 + * This function is part of find_unresolved_deltas(). There are two
520 + * walkers going in the opposite ways.
521 + *
522 + * The first one in find_unresolved_deltas() traverses down from
523 + * parent node to children, deflating nodes along the way. However,
524 + * memory for deflated nodes is limited by delta_base_cache_limit, so
525 + * at some point parent node's deflated content may be freed.
526 + *
527 + * The second walker is this function, which goes from current node up
528 + * to top parent if necessary to deflate the node. In normal
529 + * situation, its parent node would be already deflated, so it just
530 + * needs to apply delta.
531 + *
532 + * In the worst case scenario, parent node is no longer deflated because
533 + * we're running out of delta_base_cache_limit; we need to re-deflate
534 + * parents, possibly up to the top base.
535 + *
536 + * All deflated objects here are subject to be freed if we exceed
537 + * delta_base_cache_limit, just like in find_unresolved_deltas(), we
538 + * just need to make sure the last node is not freed.
539 + */
540 static void *get_base_data(struct base_data *c)
541 {
542 if (!c->data) {
543 struct object_entry *obj = c->obj;
544 + struct base_data **delta = NULL;
545 + int delta_nr = 0, delta_alloc = 0;
546
523 - if (is_delta_type(obj->type)) {
524 - void *base = get_base_data(c->base);
525 - void *raw = get_data_from_pack(obj);
547 + while (is_delta_type(c->obj->type) && !c->data) {
548 + ALLOC_GROW(delta, delta_nr + 1, delta_alloc);
549 + delta[delta_nr++] = c;
550 + c = c->base;
551 + }
552 + if (!delta_nr) {
553 + c->data = get_data_from_pack(obj);
554 + c->size = obj->size;
555 + base_cache_used += c->size;
556 + prune_base_data(c);
557 + }
558 + for (; delta_nr > 0; delta_nr--) {
559 + void *base, *raw;
560 + c = delta[delta_nr - 1];
561 + obj = c->obj;
562 + base = get_base_data(c->base);
563 + raw = get_data_from_pack(obj);
564 c->data = patch_delta(
565 base, c->base->size,
566 raw, obj->size,
@@ -530,13 +568,10 @@ static void *get_base_data(struct base_data *c)
568 free(raw);
569 if (!c->data)
570 bad_object(obj->idx.offset, "failed to apply delta");
533 - } else {
534 - c->data = get_data_from_pack(obj);
535 - c->size = obj->size;
571 + base_cache_used += c->size;
572 + prune_base_data(c);
573 }
537 -
538 - base_cache_used += c->size;
539 - prune_base_data(c);
574 + free(delta);
575 }
576 return c->data;
577 }