diff options
Diffstat (limited to 'src/lib/seq-set-builder.c')
-rw-r--r-- | src/lib/seq-set-builder.c | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/src/lib/seq-set-builder.c b/src/lib/seq-set-builder.c new file mode 100644 index 0000000..b5649b7 --- /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); +} |