mqtt5_topic_alias.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/mqtt/private/v5/mqtt5_topic_alias.h>
  6. #include <aws/common/lru_cache.h>
  7. #include <aws/common/string.h>
  8. #include <aws/mqtt/private/v5/mqtt5_utils.h>
  9. int aws_mqtt5_inbound_topic_alias_resolver_init(
  10. struct aws_mqtt5_inbound_topic_alias_resolver *resolver,
  11. struct aws_allocator *allocator) {
  12. AWS_ZERO_STRUCT(*resolver);
  13. resolver->allocator = allocator;
  14. if (aws_array_list_init_dynamic(&resolver->topic_aliases, allocator, 0, sizeof(struct aws_string *))) {
  15. return AWS_OP_ERR;
  16. }
  17. return AWS_OP_SUCCESS;
  18. }
  19. static void s_release_aliases(struct aws_mqtt5_inbound_topic_alias_resolver *resolver) {
  20. if (!aws_array_list_is_valid(&resolver->topic_aliases)) {
  21. return;
  22. }
  23. size_t cache_size = aws_array_list_length(&resolver->topic_aliases);
  24. for (size_t i = 0; i < cache_size; ++i) {
  25. struct aws_string *topic = NULL;
  26. aws_array_list_get_at(&resolver->topic_aliases, &topic, i);
  27. aws_string_destroy(topic);
  28. }
  29. }
  30. void aws_mqtt5_inbound_topic_alias_resolver_clean_up(struct aws_mqtt5_inbound_topic_alias_resolver *resolver) {
  31. s_release_aliases(resolver);
  32. aws_array_list_clean_up(&resolver->topic_aliases);
  33. }
  34. int aws_mqtt5_inbound_topic_alias_resolver_reset(
  35. struct aws_mqtt5_inbound_topic_alias_resolver *resolver,
  36. uint16_t cache_size) {
  37. aws_mqtt5_inbound_topic_alias_resolver_clean_up(resolver);
  38. AWS_ZERO_STRUCT(resolver->topic_aliases);
  39. if (aws_array_list_init_dynamic(
  40. &resolver->topic_aliases, resolver->allocator, cache_size, sizeof(struct aws_string *))) {
  41. return AWS_OP_ERR;
  42. }
  43. for (size_t i = 0; i < cache_size; ++i) {
  44. struct aws_string *topic = NULL;
  45. aws_array_list_push_back(&resolver->topic_aliases, &topic);
  46. }
  47. return AWS_OP_SUCCESS;
  48. }
  49. int aws_mqtt5_inbound_topic_alias_resolver_resolve_alias(
  50. struct aws_mqtt5_inbound_topic_alias_resolver *resolver,
  51. uint16_t alias,
  52. struct aws_byte_cursor *topic_out) {
  53. size_t cache_size = aws_array_list_length(&resolver->topic_aliases);
  54. if (alias > cache_size || alias == 0) {
  55. return aws_raise_error(AWS_ERROR_MQTT5_INVALID_INBOUND_TOPIC_ALIAS);
  56. }
  57. size_t alias_index = alias - 1;
  58. struct aws_string *topic = NULL;
  59. aws_array_list_get_at(&resolver->topic_aliases, &topic, alias_index);
  60. if (topic == NULL) {
  61. return aws_raise_error(AWS_ERROR_MQTT5_INVALID_INBOUND_TOPIC_ALIAS);
  62. }
  63. *topic_out = aws_byte_cursor_from_string(topic);
  64. return AWS_OP_SUCCESS;
  65. }
  66. int aws_mqtt5_inbound_topic_alias_resolver_register_alias(
  67. struct aws_mqtt5_inbound_topic_alias_resolver *resolver,
  68. uint16_t alias,
  69. struct aws_byte_cursor topic) {
  70. size_t cache_size = aws_array_list_length(&resolver->topic_aliases);
  71. if (alias > cache_size || alias == 0) {
  72. return aws_raise_error(AWS_ERROR_MQTT5_INVALID_INBOUND_TOPIC_ALIAS);
  73. }
  74. struct aws_string *new_entry = aws_string_new_from_cursor(resolver->allocator, &topic);
  75. if (new_entry == NULL) {
  76. return AWS_OP_ERR;
  77. }
  78. size_t alias_index = alias - 1;
  79. struct aws_string *existing_entry = NULL;
  80. aws_array_list_get_at(&resolver->topic_aliases, &existing_entry, alias_index);
  81. aws_string_destroy(existing_entry);
  82. aws_array_list_set_at(&resolver->topic_aliases, &new_entry, alias_index);
  83. return AWS_OP_SUCCESS;
  84. }
  85. /****************************************************************************************************************/
  86. struct aws_mqtt5_outbound_topic_alias_resolver_vtable {
  87. void (*destroy_fn)(struct aws_mqtt5_outbound_topic_alias_resolver *);
  88. int (*reset_fn)(struct aws_mqtt5_outbound_topic_alias_resolver *, uint16_t);
  89. int (*resolve_outbound_publish_fn)(
  90. struct aws_mqtt5_outbound_topic_alias_resolver *,
  91. const struct aws_mqtt5_packet_publish_view *,
  92. uint16_t *,
  93. struct aws_byte_cursor *);
  94. };
  95. struct aws_mqtt5_outbound_topic_alias_resolver {
  96. struct aws_allocator *allocator;
  97. struct aws_mqtt5_outbound_topic_alias_resolver_vtable *vtable;
  98. void *impl;
  99. };
  100. static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_disabled_new(
  101. struct aws_allocator *allocator);
  102. static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_lru_new(
  103. struct aws_allocator *allocator);
  104. static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_user_new(
  105. struct aws_allocator *allocator);
  106. struct aws_mqtt5_outbound_topic_alias_resolver *aws_mqtt5_outbound_topic_alias_resolver_new(
  107. struct aws_allocator *allocator,
  108. enum aws_mqtt5_client_outbound_topic_alias_behavior_type outbound_alias_behavior) {
  109. switch (aws_mqtt5_outbound_topic_alias_behavior_type_to_non_default(outbound_alias_behavior)) {
  110. case AWS_MQTT5_COTABT_USER:
  111. return s_aws_mqtt5_outbound_topic_alias_resolver_user_new(allocator);
  112. case AWS_MQTT5_COTABT_LRU:
  113. return s_aws_mqtt5_outbound_topic_alias_resolver_lru_new(allocator);
  114. case AWS_MQTT5_COTABT_DISABLED:
  115. return s_aws_mqtt5_outbound_topic_alias_resolver_disabled_new(allocator);
  116. default:
  117. return NULL;
  118. }
  119. }
  120. void aws_mqtt5_outbound_topic_alias_resolver_destroy(struct aws_mqtt5_outbound_topic_alias_resolver *resolver) {
  121. if (resolver == NULL) {
  122. return;
  123. }
  124. (*resolver->vtable->destroy_fn)(resolver);
  125. }
  126. int aws_mqtt5_outbound_topic_alias_resolver_reset(
  127. struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
  128. uint16_t topic_alias_maximum) {
  129. if (resolver == NULL) {
  130. return AWS_OP_ERR;
  131. }
  132. return (*resolver->vtable->reset_fn)(resolver, topic_alias_maximum);
  133. }
  134. int aws_mqtt5_outbound_topic_alias_resolver_resolve_outbound_publish(
  135. struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
  136. const struct aws_mqtt5_packet_publish_view *publish_view,
  137. uint16_t *topic_alias_out,
  138. struct aws_byte_cursor *topic_out) {
  139. if (resolver == NULL) {
  140. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  141. }
  142. return (*resolver->vtable->resolve_outbound_publish_fn)(resolver, publish_view, topic_alias_out, topic_out);
  143. }
  144. /*
  145. * Disabled resolver
  146. */
  147. static void s_aws_mqtt5_outbound_topic_alias_resolver_disabled_destroy(
  148. struct aws_mqtt5_outbound_topic_alias_resolver *resolver) {
  149. if (resolver == NULL) {
  150. return;
  151. }
  152. aws_mem_release(resolver->allocator, resolver);
  153. }
  154. static int s_aws_mqtt5_outbound_topic_alias_resolver_disabled_reset(
  155. struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
  156. uint16_t topic_alias_maximum) {
  157. (void)resolver;
  158. (void)topic_alias_maximum;
  159. return AWS_OP_SUCCESS;
  160. }
  161. static int s_aws_mqtt5_outbound_topic_alias_resolver_disabled_resolve_outbound_publish_fn(
  162. struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
  163. const struct aws_mqtt5_packet_publish_view *publish_view,
  164. uint16_t *topic_alias_out,
  165. struct aws_byte_cursor *topic_out) {
  166. (void)resolver;
  167. if (publish_view->topic.len == 0) {
  168. return aws_raise_error(AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION);
  169. }
  170. *topic_alias_out = 0;
  171. *topic_out = publish_view->topic;
  172. return AWS_OP_SUCCESS;
  173. }
  174. static struct aws_mqtt5_outbound_topic_alias_resolver_vtable s_aws_mqtt5_outbound_topic_alias_resolver_disabled_vtable =
  175. {
  176. .destroy_fn = s_aws_mqtt5_outbound_topic_alias_resolver_disabled_destroy,
  177. .reset_fn = s_aws_mqtt5_outbound_topic_alias_resolver_disabled_reset,
  178. .resolve_outbound_publish_fn = s_aws_mqtt5_outbound_topic_alias_resolver_disabled_resolve_outbound_publish_fn,
  179. };
  180. static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_disabled_new(
  181. struct aws_allocator *allocator) {
  182. struct aws_mqtt5_outbound_topic_alias_resolver *resolver =
  183. aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_outbound_topic_alias_resolver));
  184. resolver->allocator = allocator;
  185. resolver->vtable = &s_aws_mqtt5_outbound_topic_alias_resolver_disabled_vtable;
  186. return resolver;
  187. }
  188. /*
  189. * User resolver
  190. *
  191. * User resolution implies the user is controlling the topic alias assignments, but we still want to validate their
  192. * actions. In particular, we track the currently valid set of aliases (based on previous outbound publishes)
  193. * and only use an alias when the submitted publish is an exact match for the current assignment.
  194. */
  195. struct aws_mqtt5_outbound_topic_alias_resolver_user {
  196. struct aws_mqtt5_outbound_topic_alias_resolver base;
  197. struct aws_array_list aliases;
  198. };
  199. static void s_cleanup_user_aliases(struct aws_mqtt5_outbound_topic_alias_resolver_user *user_resolver) {
  200. for (size_t i = 0; i < aws_array_list_length(&user_resolver->aliases); ++i) {
  201. struct aws_string *alias = NULL;
  202. aws_array_list_get_at(&user_resolver->aliases, &alias, i);
  203. aws_string_destroy(alias);
  204. }
  205. aws_array_list_clean_up(&user_resolver->aliases);
  206. AWS_ZERO_STRUCT(user_resolver->aliases);
  207. }
  208. static void s_aws_mqtt5_outbound_topic_alias_resolver_user_destroy(
  209. struct aws_mqtt5_outbound_topic_alias_resolver *resolver) {
  210. if (resolver == NULL) {
  211. return;
  212. }
  213. struct aws_mqtt5_outbound_topic_alias_resolver_user *user_resolver = resolver->impl;
  214. s_cleanup_user_aliases(user_resolver);
  215. aws_mem_release(resolver->allocator, user_resolver);
  216. }
  217. static int s_aws_mqtt5_outbound_topic_alias_resolver_user_reset(
  218. struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
  219. uint16_t topic_alias_maximum) {
  220. struct aws_mqtt5_outbound_topic_alias_resolver_user *user_resolver = resolver->impl;
  221. s_cleanup_user_aliases(user_resolver);
  222. aws_array_list_init_dynamic(
  223. &user_resolver->aliases, resolver->allocator, topic_alias_maximum, sizeof(struct aws_string *));
  224. for (size_t i = 0; i < topic_alias_maximum; ++i) {
  225. struct aws_string *invalid_alias = NULL;
  226. aws_array_list_push_back(&user_resolver->aliases, &invalid_alias);
  227. }
  228. return AWS_OP_SUCCESS;
  229. }
  230. static int s_aws_mqtt5_outbound_topic_alias_resolver_user_resolve_outbound_publish_fn(
  231. struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
  232. const struct aws_mqtt5_packet_publish_view *publish_view,
  233. uint16_t *topic_alias_out,
  234. struct aws_byte_cursor *topic_out) {
  235. if (publish_view->topic_alias == NULL) {
  236. /* not using a topic alias, nothing to do */
  237. *topic_alias_out = 0;
  238. *topic_out = publish_view->topic;
  239. return AWS_OP_SUCCESS;
  240. }
  241. uint16_t user_alias = *publish_view->topic_alias;
  242. if (user_alias == 0) {
  243. /* should have been caught by publish validation */
  244. return aws_raise_error(AWS_ERROR_MQTT5_INVALID_OUTBOUND_TOPIC_ALIAS);
  245. }
  246. struct aws_mqtt5_outbound_topic_alias_resolver_user *user_resolver = resolver->impl;
  247. uint16_t user_alias_index = user_alias - 1;
  248. if (user_alias_index >= aws_array_list_length(&user_resolver->aliases)) {
  249. /* should have been caught by dynamic publish validation */
  250. return aws_raise_error(AWS_ERROR_MQTT5_INVALID_OUTBOUND_TOPIC_ALIAS);
  251. }
  252. struct aws_string *current_assignment = NULL;
  253. aws_array_list_get_at(&user_resolver->aliases, &current_assignment, user_alias_index);
  254. *topic_alias_out = user_alias;
  255. bool can_use_alias = false;
  256. if (current_assignment != NULL) {
  257. struct aws_byte_cursor assignment_cursor = aws_byte_cursor_from_string(current_assignment);
  258. if (aws_byte_cursor_eq(&assignment_cursor, &publish_view->topic)) {
  259. can_use_alias = true;
  260. }
  261. }
  262. if (can_use_alias) {
  263. AWS_ZERO_STRUCT(*topic_out);
  264. } else {
  265. *topic_out = publish_view->topic;
  266. }
  267. /* mark this alias as seen */
  268. if (!can_use_alias) {
  269. aws_string_destroy(current_assignment);
  270. current_assignment = aws_string_new_from_cursor(resolver->allocator, &publish_view->topic);
  271. aws_array_list_set_at(&user_resolver->aliases, &current_assignment, user_alias_index);
  272. }
  273. return AWS_OP_SUCCESS;
  274. }
  275. static struct aws_mqtt5_outbound_topic_alias_resolver_vtable s_aws_mqtt5_outbound_topic_alias_resolver_user_vtable = {
  276. .destroy_fn = s_aws_mqtt5_outbound_topic_alias_resolver_user_destroy,
  277. .reset_fn = s_aws_mqtt5_outbound_topic_alias_resolver_user_reset,
  278. .resolve_outbound_publish_fn = s_aws_mqtt5_outbound_topic_alias_resolver_user_resolve_outbound_publish_fn,
  279. };
  280. static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_user_new(
  281. struct aws_allocator *allocator) {
  282. struct aws_mqtt5_outbound_topic_alias_resolver_user *resolver =
  283. aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_outbound_topic_alias_resolver_user));
  284. resolver->base.allocator = allocator;
  285. resolver->base.vtable = &s_aws_mqtt5_outbound_topic_alias_resolver_user_vtable;
  286. resolver->base.impl = resolver;
  287. aws_array_list_init_dynamic(&resolver->aliases, allocator, 0, sizeof(struct aws_string *));
  288. return &resolver->base;
  289. }
  290. /*
  291. * LRU resolver
  292. *
  293. * This resolver uses an LRU cache to automatically create topic alias assignments for the user. With a reasonable
  294. * cache size, this should perform well for the majority of MQTT workloads. For workloads it does not perform well
  295. * with, the user should control the assignment (or disable entirely). Even for workloads where the LRU cache fails
  296. * to reuse an assignment every single time, the overall cost is 3 extra bytes per publish. As a rough estimate, this
  297. * means that LRU topic aliasing is "worth it" if an existing alias can be used at least once every
  298. * (AverageTopicLength / 3) publishes.
  299. */
  300. struct aws_mqtt5_outbound_topic_alias_resolver_lru {
  301. struct aws_mqtt5_outbound_topic_alias_resolver base;
  302. struct aws_cache *lru_cache;
  303. size_t max_aliases;
  304. };
  305. static void s_aws_mqtt5_outbound_topic_alias_resolver_lru_destroy(
  306. struct aws_mqtt5_outbound_topic_alias_resolver *resolver) {
  307. if (resolver == NULL) {
  308. return;
  309. }
  310. struct aws_mqtt5_outbound_topic_alias_resolver_lru *lru_resolver = resolver->impl;
  311. if (lru_resolver->lru_cache != NULL) {
  312. aws_cache_destroy(lru_resolver->lru_cache);
  313. }
  314. aws_mem_release(resolver->allocator, lru_resolver);
  315. }
  316. struct aws_topic_alias_assignment {
  317. struct aws_byte_cursor topic_cursor;
  318. struct aws_byte_buf topic;
  319. uint16_t alias;
  320. struct aws_allocator *allocator;
  321. };
  322. static void s_aws_topic_alias_assignment_destroy(struct aws_topic_alias_assignment *alias_assignment) {
  323. if (alias_assignment == NULL) {
  324. return;
  325. }
  326. aws_byte_buf_clean_up(&alias_assignment->topic);
  327. aws_mem_release(alias_assignment->allocator, alias_assignment);
  328. }
  329. static struct aws_topic_alias_assignment *s_aws_topic_alias_assignment_new(
  330. struct aws_allocator *allocator,
  331. struct aws_byte_cursor topic,
  332. uint16_t alias) {
  333. struct aws_topic_alias_assignment *assignment =
  334. aws_mem_calloc(allocator, 1, sizeof(struct aws_topic_alias_assignment));
  335. assignment->allocator = allocator;
  336. assignment->alias = alias;
  337. if (aws_byte_buf_init_copy_from_cursor(&assignment->topic, allocator, topic)) {
  338. goto on_error;
  339. }
  340. assignment->topic_cursor = aws_byte_cursor_from_buf(&assignment->topic);
  341. return assignment;
  342. on_error:
  343. s_aws_topic_alias_assignment_destroy(assignment);
  344. return NULL;
  345. }
  346. static void s_destroy_assignment_value(void *value) {
  347. s_aws_topic_alias_assignment_destroy(value);
  348. }
  349. static bool s_topic_hash_equality_fn(const void *a, const void *b) {
  350. const struct aws_byte_cursor *a_cursor = a;
  351. const struct aws_byte_cursor *b_cursor = b;
  352. return aws_byte_cursor_eq(a_cursor, b_cursor);
  353. }
  354. static int s_aws_mqtt5_outbound_topic_alias_resolver_lru_reset(
  355. struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
  356. uint16_t topic_alias_maximum) {
  357. struct aws_mqtt5_outbound_topic_alias_resolver_lru *lru_resolver = resolver->impl;
  358. if (lru_resolver->lru_cache != NULL) {
  359. aws_cache_destroy(lru_resolver->lru_cache);
  360. lru_resolver->lru_cache = NULL;
  361. }
  362. if (topic_alias_maximum > 0) {
  363. lru_resolver->lru_cache = aws_cache_new_lru(
  364. lru_resolver->base.allocator,
  365. aws_hash_byte_cursor_ptr,
  366. s_topic_hash_equality_fn,
  367. NULL,
  368. s_destroy_assignment_value,
  369. topic_alias_maximum);
  370. }
  371. lru_resolver->max_aliases = topic_alias_maximum;
  372. return AWS_OP_SUCCESS;
  373. }
  374. static int s_aws_mqtt5_outbound_topic_alias_resolver_lru_resolve_outbound_publish_fn(
  375. struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
  376. const struct aws_mqtt5_packet_publish_view *publish_view,
  377. uint16_t *topic_alias_out,
  378. struct aws_byte_cursor *topic_out) {
  379. /* No cache => no aliasing done */
  380. struct aws_mqtt5_outbound_topic_alias_resolver_lru *lru_resolver = resolver->impl;
  381. if (lru_resolver->lru_cache == NULL || lru_resolver->max_aliases == 0) {
  382. *topic_alias_out = 0;
  383. *topic_out = publish_view->topic;
  384. return AWS_OP_SUCCESS;
  385. }
  386. /* Look for the topic in the cache */
  387. struct aws_byte_cursor topic = publish_view->topic;
  388. void *existing_element = NULL;
  389. if (aws_cache_find(lru_resolver->lru_cache, &topic, &existing_element)) {
  390. return AWS_OP_ERR;
  391. }
  392. struct aws_topic_alias_assignment *existing_assignment = existing_element;
  393. if (existing_assignment != NULL) {
  394. /*
  395. * Topic exists, so use the assignment. The LRU cache find implementation has already promoted the element
  396. * to MRU.
  397. */
  398. *topic_alias_out = existing_assignment->alias;
  399. AWS_ZERO_STRUCT(*topic_out);
  400. return AWS_OP_SUCCESS;
  401. }
  402. /* Topic doesn't exist in the cache. */
  403. uint16_t new_alias_id = 0;
  404. size_t assignment_count = aws_cache_get_element_count(lru_resolver->lru_cache);
  405. if (assignment_count == lru_resolver->max_aliases) {
  406. /*
  407. * The cache is full. Get the LRU element to figure out what id we're going to reuse. There's no way to get
  408. * the LRU element without promoting it. So we get the element, save the discovered alias id, then remove
  409. * the element.
  410. */
  411. void *lru_element = aws_lru_cache_use_lru_element(lru_resolver->lru_cache);
  412. struct aws_topic_alias_assignment *replaced_assignment = lru_element;
  413. new_alias_id = replaced_assignment->alias;
  414. struct aws_byte_cursor replaced_topic = replaced_assignment->topic_cursor;
  415. /*
  416. * This is a little uncomfortable but valid. The cursor we're passing in will get invalidated (and the backing
  417. * memory deleted) as part of the removal process but it is only used to find the element to remove. Once
  418. * destruction begins it is no longer accessed.
  419. */
  420. aws_cache_remove(lru_resolver->lru_cache, &replaced_topic);
  421. } else {
  422. /*
  423. * The cache never shrinks and the first N adds are the N valid topic aliases. Since the cache isn't full,
  424. * we know the next alias that hasn't been used. This invariant only holds given that we will tear down
  425. * the connection (invalidating the cache) on errors from this function (ie, continuing on from a put
  426. * error would break the invariant and create duplicated ids).
  427. */
  428. new_alias_id = (uint16_t)(assignment_count + 1);
  429. }
  430. /*
  431. * We have a topic alias to use. Add our new assignment.
  432. */
  433. struct aws_topic_alias_assignment *new_assignment =
  434. s_aws_topic_alias_assignment_new(resolver->allocator, topic, new_alias_id);
  435. if (new_assignment == NULL) {
  436. return AWS_OP_ERR;
  437. }
  438. /* the LRU cache put implementation automatically makes the newly added element MRU */
  439. if (aws_cache_put(lru_resolver->lru_cache, &new_assignment->topic_cursor, new_assignment)) {
  440. s_aws_topic_alias_assignment_destroy(new_assignment);
  441. return AWS_OP_ERR;
  442. }
  443. *topic_alias_out = new_assignment->alias;
  444. *topic_out = topic; /* this is a new assignment so topic must go out too */
  445. return AWS_OP_SUCCESS;
  446. }
  447. static struct aws_mqtt5_outbound_topic_alias_resolver_vtable s_aws_mqtt5_outbound_topic_alias_resolver_lru_vtable = {
  448. .destroy_fn = s_aws_mqtt5_outbound_topic_alias_resolver_lru_destroy,
  449. .reset_fn = s_aws_mqtt5_outbound_topic_alias_resolver_lru_reset,
  450. .resolve_outbound_publish_fn = s_aws_mqtt5_outbound_topic_alias_resolver_lru_resolve_outbound_publish_fn,
  451. };
  452. static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_lru_new(
  453. struct aws_allocator *allocator) {
  454. struct aws_mqtt5_outbound_topic_alias_resolver_lru *resolver =
  455. aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_outbound_topic_alias_resolver_lru));
  456. resolver->base.allocator = allocator;
  457. resolver->base.vtable = &s_aws_mqtt5_outbound_topic_alias_resolver_lru_vtable;
  458. resolver->base.impl = resolver;
  459. return &resolver->base;
  460. }