/* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "seq-range-array.h" static bool seq_range_is_overflowed(const ARRAY_TYPE(seq_range) *array) { const struct seq_range *range; unsigned int count; range = array_get(array, &count); return count == 1 && range[0].seq1 == 0 && range[0].seq2 == (uint32_t)-1; } static bool ATTR_NOWARN_UNUSED_RESULT seq_range_lookup(const ARRAY_TYPE(seq_range) *array, uint32_t seq, unsigned int *idx_r) { const struct seq_range *data; unsigned int idx, left_idx, right_idx, count; data = array_get(array, &count); i_assert(count < INT_MAX); idx = 0; left_idx = 0; right_idx = count; while (left_idx < right_idx) { idx = (left_idx + right_idx) / 2; if (data[idx].seq1 <= seq) { if (data[idx].seq2 >= seq) { /* it's already in the range */ *idx_r = idx; return TRUE; } left_idx = idx+1; } else { right_idx = idx; } } if (left_idx > idx) idx++; *idx_r = idx; return FALSE; } static bool seq_range_array_add_slow_path(ARRAY_TYPE(seq_range) *array, uint32_t seq) { struct seq_range *data, value; unsigned int idx, count; value.seq1 = value.seq2 = seq; data = array_get_modifiable(array, &count); /* somewhere in the middle, array is sorted so find it with binary search */ if (seq_range_lookup(array, seq, &idx)) return TRUE; /* idx == count couldn't happen because we already handle it above */ i_assert(idx < count && data[idx].seq1 >= seq); i_assert(data[idx].seq1 > seq || data[idx].seq2 < seq); if (data[idx].seq1 == seq+1) { data[idx].seq1 = seq; if (idx > 0 && data[idx-1].seq2 == seq-1) { /* merge */ data[idx-1].seq2 = data[idx].seq2; array_delete(array, idx, 1); } } else { if (idx > 0 && data[idx-1].seq2 == seq-1) idx--; if (data[idx].seq2 == seq-1) { i_assert(idx+1 < count); /* already handled above */ data[idx].seq2 = seq; if (data[idx+1].seq1 == seq+1) { /* merge */ data[idx+1].seq1 = data[idx].seq1; array_delete(array, idx, 1); } } else { array_insert(array, idx, &value, 1); } } return FALSE; } bool seq_range_array_add(ARRAY_TYPE(seq_range) *array, uint32_t seq) { struct seq_range *data, value; unsigned int count; bool exists = FALSE; value.seq1 = value.seq2 = seq; data = array_get_modifiable(array, &count); /* quick checks */ if (count == 0) array_push_back(array, &value); else if (data[count-1].seq2 < seq) { if (data[count-1].seq2 == seq-1) { /* grow last range */ data[count-1].seq2 = seq; } else { array_push_back(array, &value); } } else if (data[0].seq1 > seq) { if (data[0].seq1-1 == seq) { /* grow down first range */ data[0].seq1 = seq; } else { array_push_front(array, &value); } } else { exists = seq_range_array_add_slow_path(array, seq); } i_assert(!seq_range_is_overflowed(array)); return exists; } void seq_range_array_add_with_init(ARRAY_TYPE(seq_range) *array, unsigned int init_count, uint32_t seq) { if (!array_is_created(array)) i_array_init(array, init_count); seq_range_array_add(array, seq); } static void seq_range_array_add_range_internal(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2, unsigned int *r_count) { struct seq_range *data, value; unsigned int idx1, idx2, count; seq_range_lookup(array, seq1, &idx1); seq_range_lookup(array, seq2, &idx2); data = array_get_modifiable(array, &count); if (r_count != NULL) { /* Find number we're adding by counting the number we're not adding, and subtracting that from the nominal range. */ unsigned int added = seq2+1 - seq1; unsigned int countidx2 = idx2; unsigned int overcounted = 0u, notadded = 0u; unsigned int i; if (idx1 == count) { /* not in a range as too far right */ } else if (seq1 < data[idx1].seq1) { /* not in a range, to the left of a real range */ } else { /* count the whole of this range, which is an overcount */ overcounted += seq1 - data[idx1].seq1; /* fencepost check: equality means the whole range is valid, therefore there's no overcounting. Result = 0 overcount */ } if (idx2 == count) { /* not in a range as too far right */ } else if (seq2 < data[idx2].seq1) { /* not in a range, to the left of a real range */ } else { /* count the whole of this range, which is an overcount */ overcounted += data[idx2].seq2 - seq2; countidx2++; /* may become == count i.e. past the end */ /* fencepost check: equality means the whole range is valid, therefore there's no overcounting. Result = 0 overcount. */ } /* Now count how many we're not adding */ for (i = idx1; i < countidx2; i++) notadded += data[i].seq2+1 - data[i].seq1; /* Maybe the not added tally included some over-counting too */ added -= (notadded - overcounted); *r_count = added; } if (idx1 > 0 && data[idx1-1].seq2+1 == seq1) idx1--; if (idx1 == idx2 && (idx2 == count || (seq2 < (uint32_t)-1 && data[idx2].seq1 > seq2+1)) && (idx1 == 0 || data[idx1-1].seq2 < seq1-1)) { /* no overlapping */ value.seq1 = seq1; value.seq2 = seq2; array_insert(array, idx1, &value, 1); } else { i_assert(idx1 < count); if (seq1 < data[idx1].seq1) data[idx1].seq1 = seq1; if (seq2 > data[idx1].seq2) { /* merge */ if (idx2 == count || data[idx2].seq1 > seq2+1) idx2--; if (seq2 >= data[idx2].seq2) { data[idx1].seq2 = seq2; } else { data[idx1].seq2 = data[idx2].seq2; } array_delete(array, idx1 + 1, idx2 - idx1); } } i_assert(!seq_range_is_overflowed(array)); } void seq_range_array_add_range(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2) { seq_range_array_add_range_internal(array, seq1, seq2, NULL); } unsigned int seq_range_array_add_range_count(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2) { unsigned int count; seq_range_array_add_range_internal(array, seq1, seq2, &count); return count; } void seq_range_array_merge(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src) { const struct seq_range *range; if (array_count(dest) == 0) { array_append_array(dest, src); return; } array_foreach(src, range) seq_range_array_add_range(dest, range->seq1, range->seq2); } void seq_range_array_merge_n(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src, unsigned int count) { const struct seq_range *src_range; unsigned int src_idx, src_count; unsigned int merge_count = count; src_range = array_get(src, &src_count); for (src_idx = 0; src_idx < src_count && merge_count > 0; src_idx++) { uint32_t first_seq = src_range[src_idx].seq1; uint32_t last_seq = src_range[src_idx].seq2; unsigned int idx_count = last_seq - first_seq + 1; if (idx_count > merge_count) { last_seq = first_seq + merge_count - 1; merge_count = 0; } else { merge_count -= idx_count; } seq_range_array_add_range(dest, first_seq, last_seq); } } bool seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq) { struct seq_range *data, value; unsigned int idx, left_idx, right_idx, count; if (!array_is_created(array)) return FALSE; data = array_get_modifiable(array, &count); if (count == 0) return FALSE; /* quick checks */ if (seq > data[count-1].seq2 || seq < data[0].seq1) { /* outside the range */ return FALSE; } if (data[count-1].seq2 == seq) { /* shrink last range */ if (data[count-1].seq1 != data[count-1].seq2) data[count-1].seq2--; else array_delete(array, count-1, 1); return TRUE; } if (data[0].seq1 == seq) { /* shrink up first range */ if (data[0].seq1 != data[0].seq2) data[0].seq1++; else array_pop_front(array); return TRUE; } /* somewhere in the middle, array is sorted so find it with binary search */ i_assert(count < INT_MAX); left_idx = 0; right_idx = count; while (left_idx < right_idx) { idx = (left_idx + right_idx) / 2; if (data[idx].seq1 > seq) right_idx = idx; else if (data[idx].seq2 < seq) left_idx = idx+1; else { /* found it */ if (data[idx].seq1 == seq) { if (data[idx].seq1 == data[idx].seq2) { /* a single sequence range. remove it entirely */ array_delete(array, idx, 1); } else { /* shrink the range */ data[idx].seq1++; } } else if (data[idx].seq2 == seq) { /* shrink the range */ data[idx].seq2--; } else { /* split the sequence range */ value.seq1 = seq + 1; value.seq2 = data[idx].seq2; data[idx].seq2 = seq - 1; array_insert(array, idx + 1, &value, 1); } return TRUE; } } return FALSE; } unsigned int seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array, uint32_t seq1, uint32_t seq2) { const struct seq_range *data; unsigned int idx, idx2, count, remove_count = 0; /* remove first and last. this makes sure that everything between can simply be deleted with array_delete(). FIXME: it would be faster if we did only one binary lookup here and handled the splitting ourself.. */ if (seq_range_array_remove(array, seq1)) remove_count++; if (seq1 == seq2) return remove_count; seq1++; if (seq_range_array_remove(array, seq2--)) remove_count++; if (seq1 > seq2) return remove_count; /* find the beginning */ data = array_get(array, &count); seq_range_lookup(array, seq1, &idx); if (idx == count) return remove_count; i_assert(data[idx].seq1 >= seq1); for (idx2 = idx; idx2 < count; idx2++) { if (data[idx2].seq1 > seq2) break; i_assert(UINT_MAX - remove_count >= seq_range_length(&data[idx2])); remove_count += seq_range_length(&data[idx2]); } array_delete(array, idx, idx2-idx); return remove_count; } unsigned int seq_range_array_remove_seq_range(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src) { unsigned int count, full_count = 0; const struct seq_range *src_range; array_foreach(src, src_range) { count = seq_range_array_remove_range(dest, src_range->seq1, src_range->seq2); i_assert(UINT_MAX - full_count >= count); full_count += count; } return full_count; } void seq_range_array_remove_nth(ARRAY_TYPE(seq_range) *array, uint32_t n, uint32_t count) { struct seq_range_iter iter; uint32_t seq1, seq2; if (count == 0) return; seq_range_array_iter_init(&iter, array); if (!seq_range_array_iter_nth(&iter, n, &seq1)) { /* n points beyond array */ return; } if (count-1 >= (uint32_t)-1 - n || !seq_range_array_iter_nth(&iter, n + (count-1), &seq2)) { /* count points beyond array */ seq2 = (uint32_t)-1; } seq_range_array_remove_range(array, seq1, seq2); } unsigned int seq_range_array_intersect(ARRAY_TYPE(seq_range) *dest, const ARRAY_TYPE(seq_range) *src) { const struct seq_range *src_range; unsigned int i, count, remove_count, full_count = 0; uint32_t last_seq = 0; src_range = array_get(src, &count); for (i = 0; i < count; i++) { if (last_seq + 1 < src_range[i].seq1) { remove_count = seq_range_array_remove_range(dest, last_seq + 1, src_range[i].seq1 - 1); i_assert(UINT_MAX - full_count >= remove_count); full_count += remove_count; } last_seq = src_range[i].seq2; } if (last_seq != (uint32_t)-1) { remove_count = seq_range_array_remove_range(dest, last_seq + 1, (uint32_t)-1); i_assert(UINT_MAX - full_count >= remove_count); full_count += remove_count; } return full_count; } bool seq_range_exists(const ARRAY_TYPE(seq_range) *array, uint32_t seq) { unsigned int idx; return seq_range_lookup(array, seq, &idx); } bool seq_range_array_have_common(const ARRAY_TYPE(seq_range) *array1, const ARRAY_TYPE(seq_range) *array2) { const struct seq_range *range1, *range2; unsigned int i1, i2, count1, count2; range1 = array_get(array1, &count1); range2 = array_get(array2, &count2); for (i1 = i2 = 0; i1 < count1 && i2 < count2; ) { if (range1[i1].seq1 <= range2[i2].seq2 && range1[i1].seq2 >= range2[i2].seq1) return TRUE; if (range1[i1].seq1 < range2[i2].seq1) i1++; else i2++; } return FALSE; } unsigned int seq_range_count(const ARRAY_TYPE(seq_range) *array) { const struct seq_range *range; unsigned int seq_count = 0; array_foreach(array, range) { i_assert(UINT_MAX - seq_count >= seq_range_length(range)); seq_count += seq_range_length(range); } return seq_count; } void seq_range_array_invert(ARRAY_TYPE(seq_range) *array, uint32_t min_seq, uint32_t max_seq) { struct seq_range *range, value; unsigned int i, count; uint32_t prev_min_seq; if (array_is_created(array)) range = array_get_modifiable(array, &count); else { range = NULL; count = 0; } if (count == 0) { /* empty -> full */ if (!array_is_created(array)) i_array_init(array, 4); value.seq1 = min_seq; value.seq2 = max_seq; array_push_back(array, &value); return; } i_assert(range[0].seq1 >= min_seq); i_assert(range[count-1].seq2 <= max_seq); if (range[0].seq1 == min_seq && range[0].seq2 == max_seq) { /* full -> empty */ array_clear(array); return; } for (i = 0; i < count; ) { prev_min_seq = min_seq; min_seq = range[i].seq2; if (range[i].seq1 == prev_min_seq) { array_delete(array, i, 1); range = array_get_modifiable(array, &count); } else { range[i].seq2 = range[i].seq1 - 1; range[i].seq1 = prev_min_seq; i++; } if (min_seq >= max_seq) { /* max_seq is reached. the rest of the array should be empty. we'll return here, because min_seq++ may wrap to 0. */ i_assert(min_seq == max_seq); i_assert(i == count); return; } min_seq++; } if (min_seq <= max_seq) { value.seq1 = min_seq; value.seq2 = max_seq; array_push_back(array, &value); } } void seq_range_array_iter_init(struct seq_range_iter *iter_r, const ARRAY_TYPE(seq_range) *array) { i_zero(iter_r); iter_r->array = array; } bool seq_range_array_iter_nth(struct seq_range_iter *iter, unsigned int n, uint32_t *seq_r) { const struct seq_range *range; unsigned int i, count, diff; if (n < iter->prev_n) { /* iterating backwards, don't bother optimizing */ iter->prev_n = 0; iter->prev_idx = 0; } range = array_get(iter->array, &count); for (i = iter->prev_idx; i < count; i++) { diff = range[i].seq2 - range[i].seq1; if (n <= iter->prev_n + diff) { i_assert(n >= iter->prev_n); *seq_r = range[i].seq1 + (n - iter->prev_n); iter->prev_idx = i; return TRUE; } iter->prev_n += diff + 1; } iter->prev_idx = i; return FALSE; }