| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#include |
|
2
|
|
|
|
|
|
|
#include "gmem.h" |
|
3
|
|
|
|
|
|
|
#include "lru.h" |
|
4
|
|
|
|
|
|
|
|
|
5
|
27
|
|
|
|
|
|
Cache* cache_build(pTHX_ int capacity) |
|
6
|
|
|
|
|
|
|
{ |
|
7
|
|
|
|
|
|
|
Cache* cache; |
|
8
|
27
|
|
|
|
|
|
GMEM_NEW(cache, Cache*, sizeof(Cache)); |
|
9
|
27
|
50
|
|
|
|
|
if (cache) { |
|
10
|
27
|
|
|
|
|
|
cache->capacity = capacity; |
|
11
|
27
|
|
|
|
|
|
cache->data = NULL; |
|
12
|
|
|
|
|
|
|
} |
|
13
|
27
|
|
|
|
|
|
return cache; |
|
14
|
|
|
|
|
|
|
} |
|
15
|
|
|
|
|
|
|
|
|
16
|
27
|
|
|
|
|
|
void cache_destroy(pTHX_ Cache* cache) |
|
17
|
|
|
|
|
|
|
{ |
|
18
|
27
|
|
|
|
|
|
cache_clear(aTHX_ cache); |
|
19
|
|
|
|
|
|
|
#if defined(GMEM_CHECK) |
|
20
|
|
|
|
|
|
|
assert(HASH_COUNT(cache->data) == 0); |
|
21
|
|
|
|
|
|
|
#endif |
|
22
|
27
|
|
|
|
|
|
GMEM_DEL(cache, Cache*, sizeof(Cache)); |
|
23
|
27
|
|
|
|
|
|
} |
|
24
|
|
|
|
|
|
|
|
|
25
|
9
|
|
|
|
|
|
int cache_size(pTHX_ Cache* cache) |
|
26
|
|
|
|
|
|
|
{ |
|
27
|
9
|
100
|
|
|
|
|
return HASH_COUNT(cache->data); |
|
28
|
|
|
|
|
|
|
} |
|
29
|
|
|
|
|
|
|
|
|
30
|
2
|
|
|
|
|
|
int cache_capacity(pTHX_ Cache* cache) |
|
31
|
|
|
|
|
|
|
{ |
|
32
|
2
|
|
|
|
|
|
return cache->capacity; |
|
33
|
|
|
|
|
|
|
} |
|
34
|
|
|
|
|
|
|
|
|
35
|
2435
|
|
|
|
|
|
static void clear_entry(pTHX_ Cache* cache, CacheEntry* entry) |
|
36
|
|
|
|
|
|
|
{ |
|
37
|
2435
|
|
|
|
|
|
SvREFCNT_dec(entry->key); |
|
38
|
2435
|
|
|
|
|
|
SvREFCNT_dec(entry->val); |
|
39
|
2435
|
50
|
|
|
|
|
HASH_DELETE(hh, cache->data, entry); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
40
|
2435
|
|
|
|
|
|
free(entry); |
|
41
|
2435
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
|
|
43
|
29
|
|
|
|
|
|
void cache_clear(pTHX_ Cache* cache) |
|
44
|
|
|
|
|
|
|
{ |
|
45
|
|
|
|
|
|
|
CacheEntry* entry; |
|
46
|
|
|
|
|
|
|
CacheEntry* tmp; |
|
47
|
2064
|
100
|
|
|
|
|
HASH_ITER(hh, cache->data, entry, tmp) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
48
|
2035
|
|
|
|
|
|
clear_entry(aTHX_ cache, entry); |
|
49
|
|
|
|
|
|
|
} |
|
50
|
29
|
|
|
|
|
|
} |
|
51
|
|
|
|
|
|
|
|
|
52
|
2430
|
|
|
|
|
|
SV* cache_find(pTHX_ Cache* cache, SV* key) |
|
53
|
|
|
|
|
|
|
{ |
|
54
|
2430
|
|
|
|
|
|
STRLEN klen = 0; |
|
55
|
2430
|
|
|
|
|
|
char* kptr = NULL; |
|
56
|
2430
|
|
|
|
|
|
kptr = SvPV(key, klen); |
|
57
|
|
|
|
|
|
|
/* fprintf(stderr, "FIND KEY %lu [%s]\n", klen, kptr); */ |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
CacheEntry* entry; |
|
60
|
11933
|
100
|
|
|
|
|
HASH_FIND(hh, cache->data, kptr, klen, entry); |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
61
|
2430
|
100
|
|
|
|
|
if (!entry) { |
|
62
|
|
|
|
|
|
|
/* return NULL; */ |
|
63
|
412
|
|
|
|
|
|
return &PL_sv_undef; |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
/* |
|
67
|
|
|
|
|
|
|
* remove it; the subsequent add will throw it on the front of the list |
|
68
|
|
|
|
|
|
|
*/ |
|
69
|
2018
|
50
|
|
|
|
|
HASH_DELETE(hh, cache->data, entry); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
70
|
4019
|
100
|
|
|
|
|
HASH_ADD_KEYPTR(hh, cache->data, kptr, klen, entry); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
|
72
|
2018
|
|
|
|
|
|
return entry->val; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
|
|
|
|
|
|
|
|
75
|
2435
|
|
|
|
|
|
int cache_add(pTHX_ Cache* cache, SV* key, SV* val) |
|
76
|
|
|
|
|
|
|
{ |
|
77
|
2435
|
|
|
|
|
|
CacheEntry* entry = (CacheEntry*) malloc(sizeof(CacheEntry)); |
|
78
|
2435
|
50
|
|
|
|
|
if (!entry) { |
|
79
|
0
|
|
|
|
|
|
return 0; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
#if 0 |
|
83
|
|
|
|
|
|
|
/* |
|
84
|
|
|
|
|
|
|
* This version simply increments the refcnt for key and val. |
|
85
|
|
|
|
|
|
|
* It is MUCH faster, and it would be awesome if we could use it. |
|
86
|
|
|
|
|
|
|
* BUT |
|
87
|
|
|
|
|
|
|
* Depending on how the key value was declared / used in Perl, |
|
88
|
|
|
|
|
|
|
* we have no guarantees that we will NOT overwrite data in the cache. |
|
89
|
|
|
|
|
|
|
*/ |
|
90
|
|
|
|
|
|
|
entry->key = key; |
|
91
|
|
|
|
|
|
|
entry->val = val; |
|
92
|
|
|
|
|
|
|
SvREFCNT_inc(entry->key); |
|
93
|
|
|
|
|
|
|
SvREFCNT_inc(entry->val); |
|
94
|
|
|
|
|
|
|
#elif 1 |
|
95
|
|
|
|
|
|
|
/* |
|
96
|
|
|
|
|
|
|
* This version creates SV* copies of key and val. |
|
97
|
|
|
|
|
|
|
* It is MUCH slower, but it might be the (only) correct way to do it. |
|
98
|
|
|
|
|
|
|
*/ |
|
99
|
2435
|
|
|
|
|
|
entry->key = newSVsv(key); |
|
100
|
2435
|
|
|
|
|
|
entry->val = newSVsv(val); |
|
101
|
|
|
|
|
|
|
#else |
|
102
|
|
|
|
|
|
|
/* |
|
103
|
|
|
|
|
|
|
* This version creates SV* copies of key only. |
|
104
|
|
|
|
|
|
|
* It is slower, and maybe it works... |
|
105
|
|
|
|
|
|
|
*/ |
|
106
|
|
|
|
|
|
|
entry->key = newSVsv(key); |
|
107
|
|
|
|
|
|
|
entry->val = val; |
|
108
|
|
|
|
|
|
|
SvREFCNT_inc(entry->val); |
|
109
|
|
|
|
|
|
|
#endif |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
/* do not use gmem for these elements, |
|
112
|
|
|
|
|
|
|
* they will be deleted internally by ut */ |
|
113
|
2435
|
|
|
|
|
|
STRLEN klen = 0; |
|
114
|
2435
|
|
|
|
|
|
char* kptr = NULL; |
|
115
|
2435
|
|
|
|
|
|
kptr = SvPV(entry->key, klen); |
|
116
|
|
|
|
|
|
|
/* fprintf(stderr, "ADD KEY %lu [%s]\n", klen, kptr); */ |
|
117
|
|
|
|
|
|
|
|
|
118
|
5107
|
100
|
|
|
|
|
HASH_ADD_KEYPTR(hh, cache->data, kptr, klen, entry); |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
/* |
|
121
|
|
|
|
|
|
|
* prune the cache to not exceed its size |
|
122
|
|
|
|
|
|
|
*/ |
|
123
|
2435
|
50
|
|
|
|
|
int size = HASH_COUNT(cache->data); |
|
124
|
|
|
|
|
|
|
int j; |
|
125
|
2835
|
100
|
|
|
|
|
for (j = cache->capacity; j < size; ++j) { |
|
126
|
|
|
|
|
|
|
CacheEntry* tmp; |
|
127
|
400
|
50
|
|
|
|
|
HASH_ITER(hh, cache->data, entry, tmp) { |
|
|
|
50
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
/* |
|
129
|
|
|
|
|
|
|
* prune the first entry; loop is based on insertion |
|
130
|
|
|
|
|
|
|
* order so this deletes the oldest item |
|
131
|
|
|
|
|
|
|
*/ |
|
132
|
|
|
|
|
|
|
|
|
133
|
400
|
|
|
|
|
|
clear_entry(aTHX_ cache, entry); |
|
134
|
400
|
|
|
|
|
|
break; |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
|
|
138
|
2435
|
|
|
|
|
|
return 1; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
2
|
|
|
|
|
|
void cache_iterate(pTHX_ Cache* cache, CacheVisitor visitor, void* arg) |
|
142
|
|
|
|
|
|
|
{ |
|
143
|
2
|
|
|
|
|
|
CacheEntry* entry = NULL; |
|
144
|
2
|
|
|
|
|
|
CacheEntry* tmp = NULL; |
|
145
|
17
|
50
|
|
|
|
|
HASH_ITER(hh, cache->data, entry, tmp) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
146
|
15
|
|
|
|
|
|
visitor(cache, entry, arg); |
|
147
|
|
|
|
|
|
|
} |
|
148
|
2
|
|
|
|
|
|
} |