1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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);
}
|