refs / iterator.con commit Merge branch 'jk/describe-omit-some-refs' into mk/describe-match-with-all (3445c3d)
   1/*
   2 * Generic reference iterator infrastructure. See refs-internal.h for
   3 * documentation about the design and use of reference iterators.
   4 */
   5
   6#include "cache.h"
   7#include "refs.h"
   8#include "refs/refs-internal.h"
   9#include "iterator.h"
  10
  11int ref_iterator_advance(struct ref_iterator *ref_iterator)
  12{
  13        return ref_iterator->vtable->advance(ref_iterator);
  14}
  15
  16int ref_iterator_peel(struct ref_iterator *ref_iterator,
  17                      struct object_id *peeled)
  18{
  19        return ref_iterator->vtable->peel(ref_iterator, peeled);
  20}
  21
  22int ref_iterator_abort(struct ref_iterator *ref_iterator)
  23{
  24        return ref_iterator->vtable->abort(ref_iterator);
  25}
  26
  27void base_ref_iterator_init(struct ref_iterator *iter,
  28                            struct ref_iterator_vtable *vtable)
  29{
  30        iter->vtable = vtable;
  31        iter->refname = NULL;
  32        iter->oid = NULL;
  33        iter->flags = 0;
  34}
  35
  36void base_ref_iterator_free(struct ref_iterator *iter)
  37{
  38        /* Help make use-after-free bugs fail quickly: */
  39        iter->vtable = NULL;
  40        free(iter);
  41}
  42
  43struct empty_ref_iterator {
  44        struct ref_iterator base;
  45};
  46
  47static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator)
  48{
  49        return ref_iterator_abort(ref_iterator);
  50}
  51
  52static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator,
  53                                   struct object_id *peeled)
  54{
  55        die("BUG: peel called for empty iterator");
  56}
  57
  58static int empty_ref_iterator_abort(struct ref_iterator *ref_iterator)
  59{
  60        base_ref_iterator_free(ref_iterator);
  61        return ITER_DONE;
  62}
  63
  64static struct ref_iterator_vtable empty_ref_iterator_vtable = {
  65        empty_ref_iterator_advance,
  66        empty_ref_iterator_peel,
  67        empty_ref_iterator_abort
  68};
  69
  70struct ref_iterator *empty_ref_iterator_begin(void)
  71{
  72        struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter));
  73        struct ref_iterator *ref_iterator = &iter->base;
  74
  75        base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable);
  76        return ref_iterator;
  77}
  78
  79int is_empty_ref_iterator(struct ref_iterator *ref_iterator)
  80{
  81        return ref_iterator->vtable == &empty_ref_iterator_vtable;
  82}
  83
  84struct merge_ref_iterator {
  85        struct ref_iterator base;
  86
  87        struct ref_iterator *iter0, *iter1;
  88
  89        ref_iterator_select_fn *select;
  90        void *cb_data;
  91
  92        /*
  93         * A pointer to iter0 or iter1 (whichever is supplying the
  94         * current value), or NULL if advance has not yet been called.
  95         */
  96        struct ref_iterator **current;
  97};
  98
  99static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
 100{
 101        struct merge_ref_iterator *iter =
 102                (struct merge_ref_iterator *)ref_iterator;
 103        int ok;
 104
 105        if (!iter->current) {
 106                /* Initialize: advance both iterators to their first entries */
 107                if ((ok = ref_iterator_advance(iter->iter0)) != ITER_OK) {
 108                        iter->iter0 = NULL;
 109                        if (ok == ITER_ERROR)
 110                                goto error;
 111                }
 112                if ((ok = ref_iterator_advance(iter->iter1)) != ITER_OK) {
 113                        iter->iter1 = NULL;
 114                        if (ok == ITER_ERROR)
 115                                goto error;
 116                }
 117        } else {
 118                /*
 119                 * Advance the current iterator past the just-used
 120                 * entry:
 121                 */
 122                if ((ok = ref_iterator_advance(*iter->current)) != ITER_OK) {
 123                        *iter->current = NULL;
 124                        if (ok == ITER_ERROR)
 125                                goto error;
 126                }
 127        }
 128
 129        /* Loop until we find an entry that we can yield. */
 130        while (1) {
 131                struct ref_iterator **secondary;
 132                enum iterator_selection selection =
 133                        iter->select(iter->iter0, iter->iter1, iter->cb_data);
 134
 135                if (selection == ITER_SELECT_DONE) {
 136                        return ref_iterator_abort(ref_iterator);
 137                } else if (selection == ITER_SELECT_ERROR) {
 138                        ref_iterator_abort(ref_iterator);
 139                        return ITER_ERROR;
 140                }
 141
 142                if ((selection & ITER_CURRENT_SELECTION_MASK) == 0) {
 143                        iter->current = &iter->iter0;
 144                        secondary = &iter->iter1;
 145                } else {
 146                        iter->current = &iter->iter1;
 147                        secondary = &iter->iter0;
 148                }
 149
 150                if (selection & ITER_SKIP_SECONDARY) {
 151                        if ((ok = ref_iterator_advance(*secondary)) != ITER_OK) {
 152                                *secondary = NULL;
 153                                if (ok == ITER_ERROR)
 154                                        goto error;
 155                        }
 156                }
 157
 158                if (selection & ITER_YIELD_CURRENT) {
 159                        iter->base.refname = (*iter->current)->refname;
 160                        iter->base.oid = (*iter->current)->oid;
 161                        iter->base.flags = (*iter->current)->flags;
 162                        return ITER_OK;
 163                }
 164        }
 165
 166error:
 167        ref_iterator_abort(ref_iterator);
 168        return ITER_ERROR;
 169}
 170
 171static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
 172                                   struct object_id *peeled)
 173{
 174        struct merge_ref_iterator *iter =
 175                (struct merge_ref_iterator *)ref_iterator;
 176
 177        if (!iter->current) {
 178                die("BUG: peel called before advance for merge iterator");
 179        }
 180        return ref_iterator_peel(*iter->current, peeled);
 181}
 182
 183static int merge_ref_iterator_abort(struct ref_iterator *ref_iterator)
 184{
 185        struct merge_ref_iterator *iter =
 186                (struct merge_ref_iterator *)ref_iterator;
 187        int ok = ITER_DONE;
 188
 189        if (iter->iter0) {
 190                if (ref_iterator_abort(iter->iter0) != ITER_DONE)
 191                        ok = ITER_ERROR;
 192        }
 193        if (iter->iter1) {
 194                if (ref_iterator_abort(iter->iter1) != ITER_DONE)
 195                        ok = ITER_ERROR;
 196        }
 197        base_ref_iterator_free(ref_iterator);
 198        return ok;
 199}
 200
 201static struct ref_iterator_vtable merge_ref_iterator_vtable = {
 202        merge_ref_iterator_advance,
 203        merge_ref_iterator_peel,
 204        merge_ref_iterator_abort
 205};
 206
 207struct ref_iterator *merge_ref_iterator_begin(
 208                struct ref_iterator *iter0, struct ref_iterator *iter1,
 209                ref_iterator_select_fn *select, void *cb_data)
 210{
 211        struct merge_ref_iterator *iter = xcalloc(1, sizeof(*iter));
 212        struct ref_iterator *ref_iterator = &iter->base;
 213
 214        /*
 215         * We can't do the same kind of is_empty_ref_iterator()-style
 216         * optimization here as overlay_ref_iterator_begin() does,
 217         * because we don't know the semantics of the select function.
 218         * It might, for example, implement "intersect" by passing
 219         * references through only if they exist in both iterators.
 220         */
 221
 222        base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable);
 223        iter->iter0 = iter0;
 224        iter->iter1 = iter1;
 225        iter->select = select;
 226        iter->cb_data = cb_data;
 227        iter->current = NULL;
 228        return ref_iterator;
 229}
 230
 231/*
 232 * A ref_iterator_select_fn that overlays the items from front on top
 233 * of those from back (like loose refs over packed refs). See
 234 * overlay_ref_iterator_begin().
 235 */
 236static enum iterator_selection overlay_iterator_select(
 237                struct ref_iterator *front, struct ref_iterator *back,
 238                void *cb_data)
 239{
 240        int cmp;
 241
 242        if (!back)
 243                return front ? ITER_SELECT_0 : ITER_SELECT_DONE;
 244        else if (!front)
 245                return ITER_SELECT_1;
 246
 247        cmp = strcmp(front->refname, back->refname);
 248
 249        if (cmp < 0)
 250                return ITER_SELECT_0;
 251        else if (cmp > 0)
 252                return ITER_SELECT_1;
 253        else
 254                return ITER_SELECT_0_SKIP_1;
 255}
 256
 257struct ref_iterator *overlay_ref_iterator_begin(
 258                struct ref_iterator *front, struct ref_iterator *back)
 259{
 260        /*
 261         * Optimization: if one of the iterators is empty, return the
 262         * other one rather than incurring the overhead of wrapping
 263         * them.
 264         */
 265        if (is_empty_ref_iterator(front)) {
 266                ref_iterator_abort(front);
 267                return back;
 268        } else if (is_empty_ref_iterator(back)) {
 269                ref_iterator_abort(back);
 270                return front;
 271        }
 272
 273        return merge_ref_iterator_begin(front, back,
 274                                        overlay_iterator_select, NULL);
 275}
 276
 277struct prefix_ref_iterator {
 278        struct ref_iterator base;
 279
 280        struct ref_iterator *iter0;
 281        char *prefix;
 282        int trim;
 283};
 284
 285static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
 286{
 287        struct prefix_ref_iterator *iter =
 288                (struct prefix_ref_iterator *)ref_iterator;
 289        int ok;
 290
 291        while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
 292                if (!starts_with(iter->iter0->refname, iter->prefix))
 293                        continue;
 294
 295                if (iter->trim) {
 296                        /*
 297                         * It is nonsense to trim off characters that
 298                         * you haven't already checked for via a
 299                         * prefix check, whether via this
 300                         * `prefix_ref_iterator` or upstream in
 301                         * `iter0`). So if there wouldn't be at least
 302                         * one character left in the refname after
 303                         * trimming, report it as a bug:
 304                         */
 305                        if (strlen(iter->iter0->refname) <= iter->trim)
 306                                die("BUG: attempt to trim too many characters");
 307                        iter->base.refname = iter->iter0->refname + iter->trim;
 308                } else {
 309                        iter->base.refname = iter->iter0->refname;
 310                }
 311
 312                iter->base.oid = iter->iter0->oid;
 313                iter->base.flags = iter->iter0->flags;
 314                return ITER_OK;
 315        }
 316
 317        iter->iter0 = NULL;
 318        if (ref_iterator_abort(ref_iterator) != ITER_DONE)
 319                return ITER_ERROR;
 320        return ok;
 321}
 322
 323static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
 324                                    struct object_id *peeled)
 325{
 326        struct prefix_ref_iterator *iter =
 327                (struct prefix_ref_iterator *)ref_iterator;
 328
 329        return ref_iterator_peel(iter->iter0, peeled);
 330}
 331
 332static int prefix_ref_iterator_abort(struct ref_iterator *ref_iterator)
 333{
 334        struct prefix_ref_iterator *iter =
 335                (struct prefix_ref_iterator *)ref_iterator;
 336        int ok = ITER_DONE;
 337
 338        if (iter->iter0)
 339                ok = ref_iterator_abort(iter->iter0);
 340        free(iter->prefix);
 341        base_ref_iterator_free(ref_iterator);
 342        return ok;
 343}
 344
 345static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
 346        prefix_ref_iterator_advance,
 347        prefix_ref_iterator_peel,
 348        prefix_ref_iterator_abort
 349};
 350
 351struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
 352                                               const char *prefix,
 353                                               int trim)
 354{
 355        struct prefix_ref_iterator *iter;
 356        struct ref_iterator *ref_iterator;
 357
 358        if (!*prefix && !trim)
 359                return iter0; /* optimization: no need to wrap iterator */
 360
 361        iter = xcalloc(1, sizeof(*iter));
 362        ref_iterator = &iter->base;
 363
 364        base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable);
 365
 366        iter->iter0 = iter0;
 367        iter->prefix = xstrdup(prefix);
 368        iter->trim = trim;
 369
 370        return ref_iterator;
 371}
 372
 373struct ref_iterator *current_ref_iter = NULL;
 374
 375int do_for_each_ref_iterator(struct ref_iterator *iter,
 376                             each_ref_fn fn, void *cb_data)
 377{
 378        int retval = 0, ok;
 379        struct ref_iterator *old_ref_iter = current_ref_iter;
 380
 381        current_ref_iter = iter;
 382        while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
 383                retval = fn(iter->refname, iter->oid, iter->flags, cb_data);
 384                if (retval) {
 385                        /*
 386                         * If ref_iterator_abort() returns ITER_ERROR,
 387                         * we ignore that error in deference to the
 388                         * callback function's return value.
 389                         */
 390                        ref_iterator_abort(iter);
 391                        goto out;
 392                }
 393        }
 394
 395out:
 396        current_ref_iter = old_ref_iter;
 397        if (ok == ITER_ERROR)
 398                return -1;
 399        return retval;
 400}