uint_set.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License 2.0 (the "License"). You may not use
  5. * this file except in compliance with the License. You can obtain a copy
  6. * in the file LICENSE in the source distribution or at
  7. * https://www.openssl.org/source/license.html
  8. */
  9. #include "internal/uint_set.h"
  10. #include "internal/common.h"
  11. #include <assert.h>
  12. /*
  13. * uint64_t Integer Sets
  14. * =====================
  15. *
  16. * This data structure supports the following operations:
  17. *
  18. * Insert Range: Adds an inclusive range of integers [start, end]
  19. * to the set. Equivalent to Insert for each number
  20. * in the range.
  21. *
  22. * Remove Range: Removes an inclusive range of integers [start, end]
  23. * from the set. Not all of the range need already be in
  24. * the set, but any part of the range in the set is removed.
  25. *
  26. * Query: Is an integer in the data structure?
  27. *
  28. * The data structure can be iterated.
  29. *
  30. * For greater efficiency in tracking large numbers of contiguous integers, we
  31. * track integer ranges rather than individual integers. The data structure
  32. * manages a list of integer ranges [[start, end]...]. Internally this is
  33. * implemented as a doubly linked sorted list of range structures, which are
  34. * automatically split and merged as necessary.
  35. *
  36. * This data structure requires O(n) traversal of the list for insertion,
  37. * removal and query when we are not adding/removing ranges which are near the
  38. * beginning or end of the set of ranges. For the applications for which this
  39. * data structure is used (e.g. QUIC PN tracking for ACK generation), it is
  40. * expected that the number of integer ranges needed at any given time will
  41. * generally be small and that most operations will be close to the beginning or
  42. * end of the range.
  43. *
  44. * Invariant: The data structure is always sorted in ascending order by value.
  45. *
  46. * Invariant: No two adjacent ranges ever 'border' one another (have no
  47. * numerical gap between them) as the data structure always ensures
  48. * such ranges are merged.
  49. *
  50. * Invariant: No two ranges ever overlap.
  51. *
  52. * Invariant: No range [a, b] ever has a > b.
  53. *
  54. * Invariant: Since ranges are represented using inclusive bounds, no range
  55. * item inside the data structure can represent a span of zero
  56. * integers.
  57. */
  58. void ossl_uint_set_init(UINT_SET *s)
  59. {
  60. ossl_list_uint_set_init(s);
  61. }
  62. void ossl_uint_set_destroy(UINT_SET *s)
  63. {
  64. UINT_SET_ITEM *x, *xnext;
  65. for (x = ossl_list_uint_set_head(s); x != NULL; x = xnext) {
  66. xnext = ossl_list_uint_set_next(x);
  67. OPENSSL_free(x);
  68. }
  69. }
  70. /* Possible merge of x, prev(x) */
  71. static void uint_set_merge_adjacent(UINT_SET *s, UINT_SET_ITEM *x)
  72. {
  73. UINT_SET_ITEM *xprev = ossl_list_uint_set_prev(x);
  74. if (xprev == NULL)
  75. return;
  76. if (x->range.start - 1 != xprev->range.end)
  77. return;
  78. x->range.start = xprev->range.start;
  79. ossl_list_uint_set_remove(s, xprev);
  80. OPENSSL_free(xprev);
  81. }
  82. static uint64_t u64_min(uint64_t x, uint64_t y)
  83. {
  84. return x < y ? x : y;
  85. }
  86. static uint64_t u64_max(uint64_t x, uint64_t y)
  87. {
  88. return x > y ? x : y;
  89. }
  90. /*
  91. * Returns 1 if there exists an integer x which falls within both ranges a and
  92. * b.
  93. */
  94. static int uint_range_overlaps(const UINT_RANGE *a,
  95. const UINT_RANGE *b)
  96. {
  97. return u64_min(a->end, b->end)
  98. >= u64_max(a->start, b->start);
  99. }
  100. static UINT_SET_ITEM *create_set_item(uint64_t start, uint64_t end)
  101. {
  102. UINT_SET_ITEM *x = OPENSSL_malloc(sizeof(UINT_SET_ITEM));
  103. if (x == NULL)
  104. return NULL;
  105. ossl_list_uint_set_init_elem(x);
  106. x->range.start = start;
  107. x->range.end = end;
  108. return x;
  109. }
  110. int ossl_uint_set_insert(UINT_SET *s, const UINT_RANGE *range)
  111. {
  112. UINT_SET_ITEM *x, *xnext, *z, *zprev, *f;
  113. uint64_t start = range->start, end = range->end;
  114. if (!ossl_assert(start <= end))
  115. return 0;
  116. if (ossl_list_uint_set_is_empty(s)) {
  117. /* Nothing in the set yet, so just add this range. */
  118. x = create_set_item(start, end);
  119. if (x == NULL)
  120. return 0;
  121. ossl_list_uint_set_insert_head(s, x);
  122. return 1;
  123. }
  124. z = ossl_list_uint_set_tail(s);
  125. if (start > z->range.end) {
  126. /*
  127. * Range is after the latest range in the set, so append.
  128. *
  129. * Note: The case where the range is before the earliest range in the
  130. * set is handled as a degenerate case of the final case below. See
  131. * optimization note (*) below.
  132. */
  133. if (z->range.end + 1 == start) {
  134. z->range.end = end;
  135. return 1;
  136. }
  137. x = create_set_item(start, end);
  138. if (x == NULL)
  139. return 0;
  140. ossl_list_uint_set_insert_tail(s, x);
  141. return 1;
  142. }
  143. f = ossl_list_uint_set_head(s);
  144. if (start <= f->range.start && end >= z->range.end) {
  145. /*
  146. * New range dwarfs all ranges in our set.
  147. *
  148. * Free everything except the first range in the set, which we scavenge
  149. * and reuse.
  150. */
  151. x = ossl_list_uint_set_head(s);
  152. x->range.start = start;
  153. x->range.end = end;
  154. for (x = ossl_list_uint_set_next(x); x != NULL; x = xnext) {
  155. xnext = ossl_list_uint_set_next(x);
  156. ossl_list_uint_set_remove(s, x);
  157. }
  158. return 1;
  159. }
  160. /*
  161. * Walk backwards since we will most often be inserting at the end. As an
  162. * optimization, test the head node first and skip iterating over the
  163. * entire list if we are inserting at the start. The assumption is that
  164. * insertion at the start and end of the space will be the most common
  165. * operations. (*)
  166. */
  167. z = end < f->range.start ? f : z;
  168. for (; z != NULL; z = zprev) {
  169. zprev = ossl_list_uint_set_prev(z);
  170. /* An existing range dwarfs our new range (optimisation). */
  171. if (z->range.start <= start && z->range.end >= end)
  172. return 1;
  173. if (uint_range_overlaps(&z->range, range)) {
  174. /*
  175. * Our new range overlaps an existing range, or possibly several
  176. * existing ranges.
  177. */
  178. UINT_SET_ITEM *ovend = z;
  179. ovend->range.end = u64_max(end, z->range.end);
  180. /* Get earliest overlapping range. */
  181. while (zprev != NULL && uint_range_overlaps(&zprev->range, range)) {
  182. z = zprev;
  183. zprev = ossl_list_uint_set_prev(z);
  184. }
  185. ovend->range.start = u64_min(start, z->range.start);
  186. /* Replace sequence of nodes z..ovend with updated ovend only. */
  187. while (z != ovend) {
  188. z = ossl_list_uint_set_next(x = z);
  189. ossl_list_uint_set_remove(s, x);
  190. OPENSSL_free(x);
  191. }
  192. break;
  193. } else if (end < z->range.start
  194. && (zprev == NULL || start > zprev->range.end)) {
  195. if (z->range.start == end + 1) {
  196. /* We can extend the following range backwards. */
  197. z->range.start = start;
  198. /*
  199. * If this closes a gap we now need to merge
  200. * consecutive nodes.
  201. */
  202. uint_set_merge_adjacent(s, z);
  203. } else if (zprev != NULL && zprev->range.end + 1 == start) {
  204. /* We can extend the preceding range forwards. */
  205. zprev->range.end = end;
  206. /*
  207. * If this closes a gap we now need to merge
  208. * consecutive nodes.
  209. */
  210. uint_set_merge_adjacent(s, z);
  211. } else {
  212. /*
  213. * The new interval is between intervals without overlapping or
  214. * touching them, so insert between, preserving sort.
  215. */
  216. x = create_set_item(start, end);
  217. if (x == NULL)
  218. return 0;
  219. ossl_list_uint_set_insert_before(s, z, x);
  220. }
  221. break;
  222. }
  223. }
  224. return 1;
  225. }
  226. int ossl_uint_set_remove(UINT_SET *s, const UINT_RANGE *range)
  227. {
  228. UINT_SET_ITEM *z, *zprev, *y;
  229. uint64_t start = range->start, end = range->end;
  230. if (!ossl_assert(start <= end))
  231. return 0;
  232. /* Walk backwards since we will most often be removing at the end. */
  233. for (z = ossl_list_uint_set_tail(s); z != NULL; z = zprev) {
  234. zprev = ossl_list_uint_set_prev(z);
  235. if (start > z->range.end)
  236. /* No overlapping ranges can exist beyond this point, so stop. */
  237. break;
  238. if (start <= z->range.start && end >= z->range.end) {
  239. /*
  240. * The range being removed dwarfs this range, so it should be
  241. * removed.
  242. */
  243. ossl_list_uint_set_remove(s, z);
  244. OPENSSL_free(z);
  245. } else if (start <= z->range.start && end >= z->range.start) {
  246. /*
  247. * The range being removed includes start of this range, but does
  248. * not cover the entire range (as this would be caught by the case
  249. * above). Shorten the range.
  250. */
  251. assert(end < z->range.end);
  252. z->range.start = end + 1;
  253. } else if (end >= z->range.end) {
  254. /*
  255. * The range being removed includes the end of this range, but does
  256. * not cover the entire range (as this would be caught by the case
  257. * above). Shorten the range. We can also stop iterating.
  258. */
  259. assert(start > z->range.start);
  260. assert(start > 0);
  261. z->range.end = start - 1;
  262. break;
  263. } else if (start > z->range.start && end < z->range.end) {
  264. /*
  265. * The range being removed falls entirely in this range, so cut it
  266. * into two. Cases where a zero-length range would be created are
  267. * handled by the above cases.
  268. */
  269. y = create_set_item(end + 1, z->range.end);
  270. ossl_list_uint_set_insert_after(s, z, y);
  271. z->range.end = start - 1;
  272. break;
  273. } else {
  274. /* Assert no partial overlap; all cases should be covered above. */
  275. assert(!uint_range_overlaps(&z->range, range));
  276. }
  277. }
  278. return 1;
  279. }
  280. int ossl_uint_set_query(const UINT_SET *s, uint64_t v)
  281. {
  282. UINT_SET_ITEM *x;
  283. if (ossl_list_uint_set_is_empty(s))
  284. return 0;
  285. for (x = ossl_list_uint_set_tail(s); x != NULL; x = ossl_list_uint_set_prev(x))
  286. if (x->range.start <= v && x->range.end >= v)
  287. return 1;
  288. else if (x->range.end < v)
  289. return 0;
  290. return 0;
  291. }