Coverage Report

Created: 2025-08-03 06:35

/src/cmark/src/references.c
Line
Count
Source (jump to first uncovered line)
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
8
12.5k
static void reference_free(cmark_reference_map *map, cmark_reference *ref) {
9
12.5k
  cmark_mem *mem = map->mem;
10
12.5k
  if (ref != NULL) {
11
12.5k
    mem->free(ref->label);
12
12.5k
    mem->free(ref->url);
13
12.5k
    mem->free(ref->title);
14
12.5k
    mem->free(ref);
15
12.5k
  }
16
12.5k
}
17
18
// normalize reference:  collapse internal whitespace to single space,
19
// remove leading/trailing whitespace, case fold
20
// Return NULL if the reference name is actually empty (i.e. composed
21
// solely from whitespace)
22
78.0k
static unsigned char *normalize_reference(cmark_mem *mem, cmark_chunk *ref) {
23
78.0k
  cmark_strbuf normalized = CMARK_BUF_INIT(mem);
24
78.0k
  unsigned char *result;
25
26
78.0k
  if (ref == NULL)
27
0
    return NULL;
28
29
78.0k
  if (ref->len == 0)
30
0
    return NULL;
31
32
78.0k
  cmark_utf8proc_case_fold(&normalized, ref->data, ref->len);
33
78.0k
  cmark_strbuf_trim(&normalized);
34
78.0k
  cmark_strbuf_normalize_whitespace(&normalized);
35
36
78.0k
  result = cmark_strbuf_detach(&normalized);
37
78.0k
  assert(result);
38
39
78.0k
  if (result[0] == '\0') {
40
1.79k
    mem->free(result);
41
1.79k
    return NULL;
42
1.79k
  }
43
44
76.2k
  return result;
45
78.0k
}
46
47
void cmark_reference_create(cmark_reference_map *map, cmark_chunk *label,
48
12.5k
                            cmark_chunk *url, cmark_chunk *title) {
49
12.5k
  cmark_reference *ref;
50
12.5k
  unsigned char *reflabel = normalize_reference(map->mem, label);
51
52
  /* empty reference name, or composed from only whitespace */
53
12.5k
  if (reflabel == NULL)
54
0
    return;
55
56
12.5k
  assert(map->sorted == NULL);
57
58
12.5k
  ref = (cmark_reference *)map->mem->calloc(1, sizeof(*ref));
59
12.5k
  ref->label = reflabel;
60
12.5k
  ref->url = cmark_clean_url(map->mem, url);
61
12.5k
  ref->title = cmark_clean_title(map->mem, title);
62
12.5k
  ref->age = map->size;
63
12.5k
  ref->next = map->refs;
64
65
12.5k
  if (ref->url != NULL)
66
12.5k
    ref->size += (int)strlen((char*)ref->url);
67
12.5k
  if (ref->title != NULL)
68
5.05k
    ref->size += (int)strlen((char*)ref->title);
69
70
12.5k
  map->refs = ref;
71
12.5k
  map->size++;
72
12.5k
}
73
74
static int
75
126k
labelcmp(const unsigned char *a, const unsigned char *b) {
76
126k
  return strcmp((const char *)a, (const char *)b);
77
126k
}
78
79
static int
80
45.1k
refcmp(const void *p1, const void *p2) {
81
45.1k
  cmark_reference *r1 = *(cmark_reference **)p1;
82
45.1k
  cmark_reference *r2 = *(cmark_reference **)p2;
83
45.1k
  int res = labelcmp(r1->label, r2->label);
84
45.1k
  return res ? res : ((int)r1->age - (int)r2->age);
85
45.1k
}
86
87
static int
88
73.1k
refsearch(const void *label, const void *p2) {
89
73.1k
  cmark_reference *ref = *(cmark_reference **)p2;
90
73.1k
  return labelcmp((const unsigned char *)label, ref->label);
91
73.1k
}
92
93
2.85k
static void sort_references(cmark_reference_map *map) {
94
2.85k
  unsigned int i = 0, last = 0, size = map->size;
95
2.85k
  cmark_reference *r = map->refs, **sorted = NULL;
96
97
2.85k
  sorted = (cmark_reference **)map->mem->calloc(size, sizeof(cmark_reference *));
98
14.2k
  while (r) {
99
11.3k
    sorted[i++] = r;
100
11.3k
    r = r->next;
101
11.3k
  }
102
103
2.85k
  qsort(sorted, size, sizeof(cmark_reference *), refcmp);
104
105
11.3k
  for (i = 1; i < size; i++) {
106
8.53k
    if (labelcmp(sorted[i]->label, sorted[last]->label) != 0)
107
539
      sorted[++last] = sorted[i];
108
8.53k
  }
109
2.85k
  map->sorted = sorted;
110
2.85k
  map->size = last + 1;
111
2.85k
}
112
113
// Returns reference if refmap contains a reference with matching
114
// label, otherwise NULL.
115
cmark_reference *cmark_reference_lookup(cmark_reference_map *map,
116
107k
                                        cmark_chunk *label) {
117
107k
  cmark_reference **ref = NULL;
118
107k
  cmark_reference *r = NULL;
119
107k
  unsigned char *norm;
120
121
107k
  if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH)
122
27.9k
    return NULL;
123
124
79.6k
  if (map == NULL || !map->size)
125
14.1k
    return NULL;
126
127
65.5k
  norm = normalize_reference(map->mem, label);
128
65.5k
  if (norm == NULL)
129
1.79k
    return NULL;
130
131
63.7k
  if (!map->sorted)
132
2.85k
    sort_references(map);
133
134
63.7k
  ref = (cmark_reference **)bsearch(norm, map->sorted, map->size, sizeof(cmark_reference *),
135
63.7k
                refsearch);
136
63.7k
  map->mem->free(norm);
137
138
63.7k
  if (ref != NULL) {
139
55.0k
    r = ref[0];
140
    /* Check for expansion limit */
141
55.0k
    if (map->max_ref_size && r->size > map->max_ref_size - map->ref_size)
142
1.00k
      return NULL;
143
54.0k
    map->ref_size += r->size;
144
54.0k
  }
145
146
62.7k
  return r;
147
63.7k
}
148
149
33.9k
void cmark_reference_map_free(cmark_reference_map *map) {
150
33.9k
  cmark_reference *ref;
151
152
33.9k
  if (map == NULL)
153
0
    return;
154
155
33.9k
  ref = map->refs;
156
46.4k
  while (ref) {
157
12.5k
    cmark_reference *next = ref->next;
158
12.5k
    reference_free(map, ref);
159
12.5k
    ref = next;
160
12.5k
  }
161
162
33.9k
  map->mem->free(map->sorted);
163
33.9k
  map->mem->free(map);
164
33.9k
}
165
166
33.9k
cmark_reference_map *cmark_reference_map_new(cmark_mem *mem) {
167
33.9k
  cmark_reference_map *map =
168
33.9k
      (cmark_reference_map *)mem->calloc(1, sizeof(cmark_reference_map));
169
33.9k
  map->mem = mem;
170
33.9k
  return map;
171
33.9k
}