]> git.ipfire.org Git - thirdparty/git.git/commitdiff
reftable: add a heap-based priority queue for reftable records
authorHan-Wen Nienhuys <hanwen@google.com>
Thu, 7 Oct 2021 20:25:10 +0000 (20:25 +0000)
committerJunio C Hamano <gitster@pobox.com>
Fri, 8 Oct 2021 17:45:48 +0000 (10:45 -0700)
This is needed to create a merged view multiple reftables

Signed-off-by: Han-Wen Nienhuys <hanwen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Makefile
reftable/pq.c [new file with mode: 0644]
reftable/pq.h [new file with mode: 0644]
reftable/pq_test.c [new file with mode: 0644]
reftable/reftable-tests.h
t/helper/test-reftable.c

index 4466aa997659fa2c2fbfb93301c339d400342181..e80f253ecc3ed60c167df24b107f4370ec54ac69 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2462,6 +2462,7 @@ REFTABLE_OBJS += reftable/block.o
 REFTABLE_OBJS += reftable/blocksource.o
 REFTABLE_OBJS += reftable/iter.o
 REFTABLE_OBJS += reftable/publicbasics.o
+REFTABLE_OBJS += reftable/pq.o
 REFTABLE_OBJS += reftable/reader.o
 REFTABLE_OBJS += reftable/record.o
 REFTABLE_OBJS += reftable/refname.o
@@ -2472,6 +2473,7 @@ REFTABLE_OBJS += reftable/writer.o
 
 REFTABLE_TEST_OBJS += reftable/basics_test.o
 REFTABLE_TEST_OBJS += reftable/block_test.o
+REFTABLE_TEST_OBJS += reftable/pq_test.o
 REFTABLE_TEST_OBJS += reftable/record_test.o
 REFTABLE_TEST_OBJS += reftable/readwrite_test.o
 REFTABLE_TEST_OBJS += reftable/test_framework.o
diff --git a/reftable/pq.c b/reftable/pq.c
new file mode 100644 (file)
index 0000000..efc4740
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#include "pq.h"
+
+#include "reftable-record.h"
+#include "system.h"
+#include "basics.h"
+
+int pq_less(struct pq_entry *a, struct pq_entry *b)
+{
+       struct strbuf ak = STRBUF_INIT;
+       struct strbuf bk = STRBUF_INIT;
+       int cmp = 0;
+       reftable_record_key(&a->rec, &ak);
+       reftable_record_key(&b->rec, &bk);
+
+       cmp = strbuf_cmp(&ak, &bk);
+
+       strbuf_release(&ak);
+       strbuf_release(&bk);
+
+       if (cmp == 0)
+               return a->index > b->index;
+
+       return cmp < 0;
+}
+
+struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq)
+{
+       return pq.heap[0];
+}
+
+int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq)
+{
+       return pq.len == 0;
+}
+
+struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq)
+{
+       int i = 0;
+       struct pq_entry e = pq->heap[0];
+       pq->heap[0] = pq->heap[pq->len - 1];
+       pq->len--;
+
+       i = 0;
+       while (i < pq->len) {
+               int min = i;
+               int j = 2 * i + 1;
+               int k = 2 * i + 2;
+               if (j < pq->len && pq_less(&pq->heap[j], &pq->heap[i])) {
+                       min = j;
+               }
+               if (k < pq->len && pq_less(&pq->heap[k], &pq->heap[min])) {
+                       min = k;
+               }
+
+               if (min == i) {
+                       break;
+               }
+
+               SWAP(pq->heap[i], pq->heap[min]);
+               i = min;
+       }
+
+       return e;
+}
+
+void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, struct pq_entry e)
+{
+       int i = 0;
+       if (pq->len == pq->cap) {
+               pq->cap = 2 * pq->cap + 1;
+               pq->heap = reftable_realloc(pq->heap,
+                                           pq->cap * sizeof(struct pq_entry));
+       }
+
+       pq->heap[pq->len++] = e;
+       i = pq->len - 1;
+       while (i > 0) {
+               int j = (i - 1) / 2;
+               if (pq_less(&pq->heap[j], &pq->heap[i])) {
+                       break;
+               }
+
+               SWAP(pq->heap[j], pq->heap[i]);
+
+               i = j;
+       }
+}
+
+void merged_iter_pqueue_release(struct merged_iter_pqueue *pq)
+{
+       int i = 0;
+       for (i = 0; i < pq->len; i++) {
+               reftable_record_destroy(&pq->heap[i].rec);
+       }
+       FREE_AND_NULL(pq->heap);
+       pq->len = pq->cap = 0;
+}
diff --git a/reftable/pq.h b/reftable/pq.h
new file mode 100644 (file)
index 0000000..56fc1b6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#ifndef PQ_H
+#define PQ_H
+
+#include "record.h"
+
+struct pq_entry {
+       int index;
+       struct reftable_record rec;
+};
+
+struct merged_iter_pqueue {
+       struct pq_entry *heap;
+       size_t len;
+       size_t cap;
+};
+
+struct pq_entry merged_iter_pqueue_top(struct merged_iter_pqueue pq);
+int merged_iter_pqueue_is_empty(struct merged_iter_pqueue pq);
+void merged_iter_pqueue_check(struct merged_iter_pqueue pq);
+struct pq_entry merged_iter_pqueue_remove(struct merged_iter_pqueue *pq);
+void merged_iter_pqueue_add(struct merged_iter_pqueue *pq, struct pq_entry e);
+void merged_iter_pqueue_release(struct merged_iter_pqueue *pq);
+int pq_less(struct pq_entry *a, struct pq_entry *b);
+
+#endif
diff --git a/reftable/pq_test.c b/reftable/pq_test.c
new file mode 100644 (file)
index 0000000..c9bb05e
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+Copyright 2020 Google LLC
+
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file or at
+https://developers.google.com/open-source/licenses/bsd
+*/
+
+#include "system.h"
+
+#include "basics.h"
+#include "constants.h"
+#include "pq.h"
+#include "record.h"
+#include "reftable-tests.h"
+#include "test_framework.h"
+
+void merged_iter_pqueue_check(struct merged_iter_pqueue pq)
+{
+       int i;
+       for (i = 1; i < pq.len; i++) {
+               int parent = (i - 1) / 2;
+
+               EXPECT(pq_less(&pq.heap[parent], &pq.heap[i]));
+       }
+}
+
+static void test_pq(void)
+{
+       char *names[54] = { NULL };
+       int N = ARRAY_SIZE(names) - 1;
+
+       struct merged_iter_pqueue pq = { NULL };
+       const char *last = NULL;
+
+       int i = 0;
+       for (i = 0; i < N; i++) {
+               char name[100];
+               snprintf(name, sizeof(name), "%02d", i);
+               names[i] = xstrdup(name);
+       }
+
+       i = 1;
+       do {
+               struct reftable_record rec =
+                       reftable_new_record(BLOCK_TYPE_REF);
+               struct pq_entry e = { 0 };
+
+               reftable_record_as_ref(&rec)->refname = names[i];
+               e.rec = rec;
+               merged_iter_pqueue_add(&pq, e);
+               merged_iter_pqueue_check(pq);
+               i = (i * 7) % N;
+       } while (i != 1);
+
+       while (!merged_iter_pqueue_is_empty(pq)) {
+               struct pq_entry e = merged_iter_pqueue_remove(&pq);
+               struct reftable_ref_record *ref =
+                       reftable_record_as_ref(&e.rec);
+
+               merged_iter_pqueue_check(pq);
+
+               if (last) {
+                       EXPECT(strcmp(last, ref->refname) < 0);
+               }
+               last = ref->refname;
+               ref->refname = NULL;
+               reftable_free(ref);
+       }
+
+       for (i = 0; i < N; i++) {
+               reftable_free(names[i]);
+       }
+
+       merged_iter_pqueue_release(&pq);
+}
+
+int pq_test_main(int argc, const char *argv[])
+{
+       RUN_TEST(test_pq);
+       return 0;
+}
index 3d541fa5c0ce893c1db4148f22e1d8c17fbfcd9e..0019cbcfa4987f8afb8769a4a33d34446e2ce4dc 100644 (file)
@@ -12,6 +12,7 @@ https://developers.google.com/open-source/licenses/bsd
 int basics_test_main(int argc, const char **argv);
 int block_test_main(int argc, const char **argv);
 int merged_test_main(int argc, const char **argv);
+int pq_test_main(int argc, const char **argv);
 int record_test_main(int argc, const char **argv);
 int refname_test_main(int argc, const char **argv);
 int readwrite_test_main(int argc, const char **argv);
index 898aba836fd170bd28ac936c9209d900063606da..0b5a1701df15cc16ca10f2d35f07e442785aee3f 100644 (file)
@@ -5,6 +5,7 @@ int cmd__reftable(int argc, const char **argv)
 {
        basics_test_main(argc, argv);
        block_test_main(argc, argv);
+       pq_test_main(argc, argv);
        record_test_main(argc, argv);
        readwrite_test_main(argc, argv);
        tree_test_main(argc, argv);