From: Markus Valentin Date: Wed, 8 Dec 2021 16:17:56 +0000 (+0100) Subject: lib: Add seq-set-builder and tests for it X-Git-Tag: 2.4.0~4315 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=433f624ccaf88d259a5ebe83bf63474e467148bd;p=thirdparty%2Fdovecot%2Fcore.git lib: Add seq-set-builder and tests for it --- diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 4fef785291..8acde95c53 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -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 index 0000000000..b5649b7e28 --- /dev/null +++ b/src/lib/seq-set-builder.c @@ -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 index 0000000000..37d9cbcc1d --- /dev/null +++ b/src/lib/seq-set-builder.h @@ -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 diff --git a/src/lib/test-lib.inc b/src/lib/test-lib.inc index 279c2c7859..e698708511 100644 --- a/src/lib/test-lib.inc +++ b/src/lib/test-lib.inc @@ -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 index 0000000000..58fd068ee2 --- /dev/null +++ b/src/lib/test-seq-set-builder.c @@ -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(); +}