]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add seq-set-builder and tests for it
authorMarkus Valentin <markus.valentin@open-xchange.com>
Wed, 8 Dec 2021 16:17:56 +0000 (17:17 +0100)
committerMarkus Valentin <markus.valentin@open-xchange.com>
Mon, 14 Mar 2022 13:06:49 +0000 (14:06 +0100)
src/lib/Makefile.am
src/lib/seq-set-builder.c [new file with mode: 0644]
src/lib/seq-set-builder.h [new file with mode: 0644]
src/lib/test-lib.inc
src/lib/test-seq-set-builder.c [new file with mode: 0644]

index 7aea6407bca63fd039bede1dc3b37c488b3f7b6d..ac5c62b102876511b9288e9d297ac46cc7b54f04 100644 (file)
@@ -172,6 +172,7 @@ liblib_la_SOURCES = \
        safe-mkstemp.c \
        sendfile-util.c \
        seq-range-array.c \
+       seq-set-builder.c \
        sha1.c \
        sha2.c \
        sha3.c \
@@ -329,6 +330,7 @@ headers = \
        safe-mkstemp.h \
        sendfile-util.h \
        seq-range-array.h \
+       seq-set-builder.h \
        sha-common.h \
        sha1.h \
        sha2.h \
@@ -446,6 +448,7 @@ test_lib_SOURCES = \
        test-priorityq.c \
        test-random.c \
        test-seq-range-array.c \
+       test-seq-set-builder.c \
        test-stats-dist.c \
        test-str.c \
        test-strescape.c \
diff --git a/src/lib/seq-set-builder.c b/src/lib/seq-set-builder.c
new file mode 100644 (file)
index 0000000..b5649b7
--- /dev/null
@@ -0,0 +1,113 @@
+/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "seq-set-builder.h"
+
+struct seqset_builder {
+       string_t *str;
+       uint32_t last_seq;
+       size_t last_seq_pos;
+       size_t prefix_length;
+};
+
+struct seqset_builder *seqset_builder_init(string_t *str)
+{
+       struct seqset_builder *builder;
+       builder = i_new(struct seqset_builder, 1);
+       builder->str = str;
+       builder->last_seq = 0;
+       builder->prefix_length = str_len(str);
+       builder->last_seq_pos = 0;
+       return builder;
+}
+
+static void
+seqset_builder_append_one(struct seqset_builder *builder, uint32_t seq)
+{
+       builder->last_seq_pos = str_len(builder->str)+1;
+       str_printfa(builder->str, "%u,", seq);
+}
+
+static void
+seqset_builder_create_or_merge_range(struct seqset_builder *builder,
+                                    uint32_t seq)
+{
+       char delimiter = '\0';
+
+       i_assert(builder->last_seq_pos > builder->prefix_length);
+
+       str_truncate(builder->str, builder->last_seq_pos-1);
+
+       /* Get the delimiter from the builder string */
+       if (str_len(builder->str) > 0 &&
+           str_len(builder->str) - 1 > builder->prefix_length)
+               delimiter = str_data(builder->str)[str_len(builder->str) - 1];
+
+       if (delimiter == ':') {
+               seqset_builder_append_one(builder, seq);
+       } else if (delimiter == ',' || delimiter == '\0') {
+               str_printfa(builder->str, "%u:", builder->last_seq);
+               builder->last_seq_pos = str_len(builder->str) + 1;
+               str_printfa(builder->str, "%u,", seq);
+       } else
+               i_unreached();
+       return;
+}
+
+void seqset_builder_add(struct seqset_builder *builder, uint32_t seq)
+{
+       if (builder->last_seq == 0) {
+               /* No seq was yet appened so just append this one */
+               seqset_builder_append_one(builder, seq);
+       } else if (builder->last_seq + 1 == seq) {
+               /* This seq is following directly on the previous one
+                  try to create a range of seqs */
+               seqset_builder_create_or_merge_range(builder, seq);
+       } else {
+               /* Append this seq without creating a range */
+               seqset_builder_append_one(builder, seq);
+       }
+       builder->last_seq = seq;
+}
+
+bool seqset_builder_try_add(struct seqset_builder *builder, size_t max_len,
+                           uint32_t seq)
+{
+       /* Length of this sequence to be appended */
+       unsigned int seq_len = 0;
+       /* Buffer to use when calculating seq length as string */
+       char seq_str[MAX_INT_STRLEN];
+       /* Current length of the seq string */
+       unsigned int builder_str_len = str_len(builder->str);
+
+       if (builder->last_seq + 1 == seq && builder_str_len + 1 <= max_len) {
+               /* Following sequence: This seq can't grow the overall length
+                  by more than one. */
+               seqset_builder_add(builder, seq);
+               return TRUE;
+       }
+
+       if (builder_str_len + MAX_INT_STRLEN + 1 <= max_len) {
+               /* Appending the maximum length of a sequence number and ','
+                  still fits into max_len. There is no need to check the
+                  actual length. */
+               seqset_builder_add(builder, seq);
+               return TRUE;
+       }
+
+       seq_len = strlen(dec2str_buf(seq_str, seq)) + 1;
+       if (seq_len + builder_str_len > max_len)
+               return FALSE;
+
+       seqset_builder_add(builder, seq);
+       return TRUE;
+}
+
+void seqset_builder_deinit(struct seqset_builder **builder)
+{
+       /* If anything was appened to the string remove the trailing ',' */
+       if ((*builder)->last_seq != 0)
+               str_truncate((*builder)->str, str_len((*builder)->str) - 1);
+       i_free(*builder);
+}
diff --git a/src/lib/seq-set-builder.h b/src/lib/seq-set-builder.h
new file mode 100644 (file)
index 0000000..37d9cbc
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef SEQ_SET_BUILDER_H
+#define SEQ_SET_BUILDER_H
+
+/* Append a seqset to the given string. */
+struct seqset_builder *seqset_builder_init(string_t *str);
+/* Add seq to the string. The string must not have been modified before the previous
+   seqset_builder_add() call, since the last sequence in it may be rewritten. */
+void seqset_builder_add(struct seqset_builder *builder, uint32_t seq);
+/* Add the seq to the string, but only if the string length stays below max_len.
+   Returns TRUE if added, FALSE if not. */
+bool seqset_builder_try_add(struct seqset_builder *builder, size_t max_len, uint32_t seq);
+/* Deinitialize the builder */
+void seqset_builder_deinit(struct seqset_builder **builder);
+
+#endif
index 279c2c7859c317d3f227b81b4c3d89c7809ea371..e698708511b0cd3ee859072435f6a65fd334370f 100644 (file)
@@ -94,6 +94,7 @@ TEST(test_random)
 FATAL(fatal_random)
 TEST(test_seq_range_array)
 FATAL(fatal_seq_range_array)
+TEST(test_seq_set_builder)
 TEST(test_stats_dist)
 TEST(test_str)
 TEST(test_strescape)
diff --git a/src/lib/test-seq-set-builder.c b/src/lib/test-seq-set-builder.c
new file mode 100644 (file)
index 0000000..58fd068
--- /dev/null
@@ -0,0 +1,131 @@
+/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "str.h"
+#include "seq-set-builder.h"
+
+static void test_seq_set_builder_add(void)
+{
+       struct seqset_builder *seq_set_builder;
+
+       test_begin("seq set builder add");
+       string_t *test_str = t_str_new(128);
+       str_append(test_str, "UID COPY ");
+       seq_set_builder = seqset_builder_init(test_str);
+       seqset_builder_add(seq_set_builder, 1);
+       seqset_builder_add(seq_set_builder, 3);
+       seqset_builder_add(seq_set_builder, 6);
+       seqset_builder_add(seq_set_builder, 7);
+       seqset_builder_add(seq_set_builder, 8);
+       seqset_builder_add(seq_set_builder, 9);
+       seqset_builder_add(seq_set_builder, 10);
+       seqset_builder_add(seq_set_builder, 12);
+       seqset_builder_deinit(&seq_set_builder);
+       test_assert_strcmp(str_c(test_str), "UID COPY 1,3,6:10,12");
+
+       str_truncate(test_str, 0);
+       seq_set_builder = seqset_builder_init(test_str);
+       seqset_builder_add(seq_set_builder, 99999);
+       seqset_builder_add(seq_set_builder, 100000);
+       seqset_builder_add(seq_set_builder, 5);
+       seqset_builder_add(seq_set_builder, 7);
+       seqset_builder_add(seq_set_builder, 9);
+       seqset_builder_add(seq_set_builder, 10);
+       seqset_builder_add(seq_set_builder, 120);
+       seqset_builder_add(seq_set_builder, 121);
+       seqset_builder_add(seq_set_builder, 122);
+       seqset_builder_add(seq_set_builder, 125);
+       seqset_builder_deinit(&seq_set_builder);
+       test_assert_strcmp(str_c(test_str), "99999:100000,5,7,9:10,120:122,125");
+
+       str_truncate(test_str, 0);
+       str_append(test_str, "UID COPY ");
+       seq_set_builder = seqset_builder_init(test_str);
+       seqset_builder_add(seq_set_builder, 287409);
+       seqset_builder_add(seq_set_builder, 287410);
+       seqset_builder_deinit(&seq_set_builder);
+       test_assert_strcmp(str_c(test_str), "UID COPY 287409:287410");
+
+       str_truncate(test_str, 0);
+       str_append(test_str, "UID COPY 287409,");
+       seq_set_builder = seqset_builder_init(test_str);
+       seqset_builder_add(seq_set_builder, 287410);
+       seqset_builder_add(seq_set_builder, 287411);
+       test_assert_strcmp(str_c(test_str), "UID COPY 287409,287410:287411,");
+       seqset_builder_deinit(&seq_set_builder);
+       test_assert_strcmp(str_c(test_str), "UID COPY 287409,287410:287411");
+
+       str_truncate(test_str, 0);
+       seq_set_builder = seqset_builder_init(test_str);
+       seqset_builder_add(seq_set_builder, 4294967289);
+       seqset_builder_add(seq_set_builder, 4294967291);
+       seqset_builder_add(seq_set_builder, 4294967293);
+       seqset_builder_add(seq_set_builder, 4294967294);
+       seqset_builder_add(seq_set_builder, 4294967295);
+       test_assert_strcmp(str_c(test_str), "4294967289,4294967291,4294967293:4294967295,");
+       seqset_builder_deinit(&seq_set_builder);
+       test_assert_strcmp(str_c(test_str), "4294967289,4294967291,4294967293:4294967295");
+
+       str_truncate(test_str, 0);
+       str_append(test_str, ";j;,");
+       seq_set_builder = seqset_builder_init(test_str);
+       test_assert_strcmp(str_c(test_str), ";j;,");
+       seqset_builder_deinit(&seq_set_builder);
+       test_assert_strcmp(str_c(test_str), ";j;,");
+
+       test_end();
+}
+
+static void test_seq_set_builder_try_add(void)
+{
+       struct seqset_builder *seq_set_builder;
+
+       test_begin("seq set builder try add");
+
+       string_t *test_str = t_str_new(128);
+       str_append(test_str, "UID MOVE ");
+
+       seq_set_builder = seqset_builder_init(test_str);
+       test_assert(seqset_builder_try_add(seq_set_builder, 20, 1));
+       test_assert(seqset_builder_try_add(seq_set_builder, 20, 3));
+       test_assert(seqset_builder_try_add(seq_set_builder, 20, 5));
+       test_assert(seqset_builder_try_add(seq_set_builder, 20, 7));
+       test_assert(seqset_builder_try_add(seq_set_builder, 20, 9));
+       test_assert(19 == str_len(test_str));
+
+       test_assert_strcmp(str_c(test_str), "UID MOVE 1,3,5,7,9,");
+
+       test_assert(!seqset_builder_try_add(seq_set_builder, 20, 11));
+       test_assert(str_len(test_str) <= 20);
+       test_assert_strcmp(str_c(test_str), "UID MOVE 1,3,5,7,9,");
+
+       test_assert(seqset_builder_try_add(seq_set_builder, 21, 2));
+       test_assert(str_len(test_str) <= 21);
+       test_assert_strcmp(str_c(test_str), "UID MOVE 1,3,5,7,9,2,");
+
+       test_assert(!seqset_builder_try_add(seq_set_builder, 20, 15));
+       test_assert(seqset_builder_try_add(seq_set_builder, 24, 13));
+       test_assert(!seqset_builder_try_add(seq_set_builder, 24, 17));
+       test_assert(str_len(test_str) <= 24);
+       test_assert_strcmp(str_c(test_str), "UID MOVE 1,3,5,7,9,2,13,");
+
+       str_truncate(test_str, 0);
+       seq_set_builder = seqset_builder_init(test_str);
+       test_assert(seqset_builder_try_add(seq_set_builder, 32, 4294967289));
+       test_assert(seqset_builder_try_add(seq_set_builder, 32, 4294967291));
+       test_assert(seqset_builder_try_add(seq_set_builder, 32, 4294967292));
+       test_assert(!seqset_builder_try_add(seq_set_builder, 32, 4294967293));
+       test_assert(seqset_builder_try_add(seq_set_builder, 50, 4294967293));
+       test_assert(seqset_builder_try_add(seq_set_builder, 50, 4294967295));
+       test_assert_strcmp(str_c(test_str), "4294967289,4294967291:4294967293,4294967295,");
+       seqset_builder_deinit(&seq_set_builder);
+       test_assert_strcmp(str_c(test_str), "4294967289,4294967291:4294967293,4294967295");
+
+       test_end();
+}
+
+void test_seq_set_builder(void)
+{
+       test_seq_set_builder_add();
+       test_seq_set_builder_try_add();
+}