references.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. #include "cmark.h"
  2. #include "utf8.h"
  3. #include "parser.h"
  4. #include "references.h"
  5. #include "inlines.h"
  6. #include "chunk.h"
  7. static unsigned int refhash(const unsigned char *link_ref) {
  8. unsigned int hash = 0;
  9. while (*link_ref)
  10. hash = (*link_ref++) + (hash << 6) + (hash << 16) - hash;
  11. return hash;
  12. }
  13. static void reference_free(cmark_reference_map *map, cmark_reference *ref) {
  14. cmark_mem *mem = map->mem;
  15. if (ref != NULL) {
  16. mem->free(ref->label);
  17. cmark_chunk_free(mem, &ref->url);
  18. cmark_chunk_free(mem, &ref->title);
  19. mem->free(ref);
  20. }
  21. }
  22. // normalize reference: collapse internal whitespace to single space,
  23. // remove leading/trailing whitespace, case fold
  24. // Return NULL if the reference name is actually empty (i.e. composed
  25. // solely from whitespace)
  26. static unsigned char *normalize_reference(cmark_mem *mem, cmark_chunk *ref) {
  27. cmark_strbuf normalized = CMARK_BUF_INIT(mem);
  28. unsigned char *result;
  29. if (ref == NULL)
  30. return NULL;
  31. if (ref->len == 0)
  32. return NULL;
  33. cmark_utf8proc_case_fold(&normalized, ref->data, ref->len);
  34. cmark_strbuf_trim(&normalized);
  35. cmark_strbuf_normalize_whitespace(&normalized);
  36. result = cmark_strbuf_detach(&normalized);
  37. assert(result);
  38. if (result[0] == '\0') {
  39. mem->free(result);
  40. return NULL;
  41. }
  42. return result;
  43. }
  44. static void add_reference(cmark_reference_map *map, cmark_reference *ref) {
  45. cmark_reference *t = ref->next = map->table[ref->hash % REFMAP_SIZE];
  46. while (t) {
  47. if (t->hash == ref->hash && !strcmp((char *)t->label, (char *)ref->label)) {
  48. reference_free(map, ref);
  49. return;
  50. }
  51. t = t->next;
  52. }
  53. map->table[ref->hash % REFMAP_SIZE] = ref;
  54. }
  55. void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label,
  56. cmark_chunk *url, cmark_chunk *title) {
  57. cmark_reference *ref;
  58. unsigned char *reflabel = normalize_reference(map->mem, label);
  59. /* empty reference name, or composed from only whitespace */
  60. if (reflabel == NULL)
  61. return;
  62. ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref));
  63. ref->label = reflabel;
  64. ref->hash = refhash(ref->label);
  65. ref->url = cmark_clean_url(map->mem, url);
  66. ref->title = cmark_clean_title(map->mem, title);
  67. ref->next = NULL;
  68. add_reference(map, ref);
  69. }
  70. // Returns reference if refmap contains a reference with matching
  71. // label, otherwise NULL.
  72. cmark_reference *cmark_reference_lookup(cmark_reference_map *map,
  73. cmark_chunk *label) {
  74. cmark_reference *ref = NULL;
  75. unsigned char *norm;
  76. unsigned int hash;
  77. if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH)
  78. return NULL;
  79. if (map == NULL)
  80. return NULL;
  81. norm = normalize_reference(map->mem, label);
  82. if (norm == NULL)
  83. return NULL;
  84. hash = refhash(norm);
  85. ref = map->table[hash % REFMAP_SIZE];
  86. while (ref) {
  87. if (ref->hash == hash && !strcmp((char *)ref->label, (char *)norm))
  88. break;
  89. ref = ref->next;
  90. }
  91. map->mem->free(norm);
  92. return ref;
  93. }
  94. void cmark_reference_map_free(cmark_reference_map *map) {
  95. unsigned int i;
  96. if (map == NULL)
  97. return;
  98. for (i = 0; i < REFMAP_SIZE; ++i) {
  99. cmark_reference *ref = map->table[i];
  100. cmark_reference *next;
  101. while (ref) {
  102. next = ref->next;
  103. reference_free(map, ref);
  104. ref = next;
  105. }
  106. }
  107. map->mem->free(map);
  108. }
  109. cmark_reference_map *cmark_reference_map_new(cmark_mem *mem) {
  110. cmark_reference_map *map =
  111. (cmark_reference_map *)mem->calloc(1, sizeof(cmark_reference_map));
  112. map->mem = mem;
  113. return map;
  114. }