summaryrefslogtreecommitdiff
path: root/src/references.c
blob: 84cb773b132fa1567dc6291919ca2a0dbf231a3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include "stmd.h"
#include "utf8.h"
#include "references.h"

static unsigned int
refhash(const unsigned char *link_ref)
{
	unsigned int hash = 0;

	while (*link_ref)
		hash = (*link_ref++) + (hash << 6) + (hash << 16) - hash;

	return hash;
}

static void reference_free(reference *ref)
{
	free(ref->label);
	free(ref->url);
	free(ref->title);
	free(ref);
}

// normalize reference:  collapse internal whitespace to single space,
// remove leading/trailing whitespace, case fold
static unsigned char *normalize_reference(chunk *ref)
{
	strbuf normalized = GH_BUF_INIT;

	utf8proc_case_fold(&normalized, ref->data, ref->len);
	strbuf_trim(&normalized);
	strbuf_normalize_whitespace(&normalized);

	return strbuf_detach(&normalized);
}

static void add_reference(reference_map *map, reference* ref)
{
	reference *t = ref->next = map->table[ref->hash % REFMAP_SIZE];

	while (t) {
		if (t->hash == ref->hash &&
			!strcmp((char *)t->label, (char *)ref->label)) {
			reference_free(ref);
			return;
		}

		t = t->next;
	}

	map->table[ref->hash % REFMAP_SIZE] = ref;
}

extern reference *reference_create(reference_map *map, chunk *label, chunk *url, chunk *title)
{
	reference *ref;
	ref = malloc(sizeof(reference));
	ref->label = normalize_reference(label);
	ref->hash = refhash(ref->label);
	ref->url = clean_url(url);
	ref->title = clean_title(title);
	ref->next = NULL;

	add_reference(map, ref);

	return ref;
}

// Returns reference if refmap contains a reference with matching
// label, otherwise NULL.
reference* reference_lookup(reference_map *map, chunk *label)
{
	reference *ref = NULL;
	unsigned char *norm;
	unsigned int hash;
	
	if (map == NULL)
		return NULL;
	
	norm = normalize_reference(label);
	hash = refhash(norm);
	ref = map->table[hash % REFMAP_SIZE];

	while (ref) {
		if (ref->hash == hash &&
			!strcmp((char *)ref->label, (char *)norm))
			break;
		ref = ref->next;
	}

	free(norm);
	return ref;
}

void reference_map_free(reference_map *map)
{
	unsigned int i;

	for (i = 0; i < REFMAP_SIZE; ++i) {
		reference *ref = map->table[i];
		reference *next;

		while (ref) {
			next = ref->next;
			reference_free(ref);
			ref = next;
		}
	}

	free(map);
}

reference_map *reference_map_new(void)
{
	reference_map *map = malloc(sizeof(reference_map));
	memset(map, 0x0, sizeof(reference_map));
	return map;
}