GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 72.6% 119 / 0 / 164
Functions: 90.0% 27 / 0 / 30
Branches: 34.9% 53 / 0 / 152

core/vartable.h
Line Branch Exec Source
1 // This program is free software; you can redistribute it and/or modify
2 // it under the terms of the GNU General Public License as published by
3 // the Free Software Foundation; either version 2 of the License, or
4 // (at your option) any later version.
5 //
6 // This program is distributed in the hope that it will be useful,
7 // but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // GNU General Public License for more details.
10 //
11 // You should have received a copy of the GNU General Public License
12 // along with this program; if not, write to the Free Software
13 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
14 // http://www.gnu.org/copyleft/gpl.html .
15 //
16 // Linking Avisynth statically or dynamically with other modules is making a
17 // combined work based on Avisynth. Thus, the terms and conditions of the GNU
18 // General Public License cover the whole combination.
19 //
20 // As a special exception, the copyright holders of Avisynth give you
21 // permission to link Avisynth with independent modules that communicate with
22 // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
23 // terms of these independent modules, and to copy and distribute the
24 // resulting combined work under terms of your choice, provided that
25 // every copy of the combined work is accompanied by a complete copy of
26 // the source code of Avisynth (the version of Avisynth used to produce the
27 // combined work), being distributed under the terms of the GNU General
28 // Public License plus this exception. An independent module is a module
29 // which is not derived from or based on Avisynth, such as 3rd-party filters,
30 // import and export plugins, or graphical user interfaces.
31
32
33 #ifndef AVSCORE_VARTABLE_H
34 #define AVSCORE_VARTABLE_H
35
36 #include "strings.h"
37 #include "avs/alignment.h"
38 #include "avs/minmax.h"
39 #include <avisynth.h>
40 #include <unordered_map>
41 #include <mutex>
42 #include <iostream>
43 #include <cstring>
44
45 struct iequal_to_ascii
46 {
47 25386 bool operator()(const char* str1, const char* str2) const
48 {
49 25386 return streqi(str1, str2);
50 }
51 };
52
53 struct ihash_ascii
54 {
55 275693 std::size_t operator()(const char* s) const
56 {
57 // NOTE the connection between the hash() and equals() functions!
58 // In order for the hash table to work correctly, if two strings compare
59 // equal, they MUST have the same hash.
60
61 275693 size_t hash = 0;
62
2/2
✓ Branch 4 → 3 taken 6699860 times.
✓ Branch 4 → 5 taken 275693 times.
6975553 while (*s)
63 6699860 hash = hash * 101 + tolower(*s++);
64
65 275693 return hash;
66 }
67 };
68
69 // Custom hash function for composite keys (const char* pointer + size_t length)
70 struct CompositeKeyDjb2Hash {
71 489317 size_t operator()(const std::pair<const char*, size_t>& key) const {
72 489317 const char* str = key.first;
73 489317 size_t len = key.second;
74 489317 size_t hash = 5381;
75
76
2/2
✓ Branch 4 → 3 taken 17968862 times.
✓ Branch 4 → 5 taken 489317 times.
18458179 while (len--) {
77 17968862 hash = ((hash << 5) + hash) + *str;
78 17968862 ++str;
79 }
80 489317 return hash;
81 }
82 };
83
84 // Custom equality function for composite keys
85 struct CompositeKeyEqual {
86 23557 bool operator()(const std::pair<const char*, size_t>& lhs, const std::pair<const char*, size_t>& rhs) const {
87
2/4
✓ Branch 2 → 3 taken 23557 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 23557 times.
✗ Branch 3 → 5 not taken.
23557 return (lhs.second == rhs.second) && (std::strncmp(lhs.first, rhs.first, lhs.second) == 0);
88 }
89 };
90
91 // This doles out storage space for strings. No space is ever freed
92 // until the class instance is destroyed (which happens when a script
93 // file is closed).
94 class StringDump {
95 enum { BLOCK_SIZE = 32768 };
96 char* current_block;
97 size_t block_pos, block_size;
98
99 // string cache
100 std::unordered_map<std::pair<const char*, size_t>, const char*, CompositeKeyDjb2Hash, CompositeKeyEqual> cache;
101
102 // Get the pointer to the string (if it exists)
103 256437 const char* get_string(const char* keyStr, size_t keyLen) {
104
1/2
✓ Branch 3 → 4 taken 256437 times.
✗ Branch 3 → 12 not taken.
256437 auto it = cache.find({ keyStr, keyLen });
105
2/2
✓ Branch 6 → 7 taken 23557 times.
✓ Branch 6 → 9 taken 232880 times.
256437 if (it != cache.end()) {
106 23557 return it->second; // Return existing pointer
107 }
108 else {
109 232880 return nullptr;
110 }
111 }
112
113 232880 void add_string(const char* keyStr, size_t keyLen) {
114
1/2
✓ Branch 3 → 4 taken 232880 times.
✗ Branch 3 → 5 not taken.
232880 cache[{keyStr, keyLen}] = keyStr;
115 232880 }
116
117 232880 void ensure_length(size_t len)
118 {
119
2/2
✓ Branch 2 → 3 taken 453 times.
✓ Branch 2 → 6 taken 232427 times.
232880 if (block_pos + len + 1 > block_size) {
120 453 char* new_block = new char[block_size = max(block_size, len + 1 + sizeof(char*))];
121 //_RPT0(0, "StringDump: Allocating new stringblock.\r\n");
122 453 *(char**)new_block = current_block; // beginning of block holds pointer to previous block
123 453 current_block = new_block;
124 453 block_pos = sizeof(char*);
125 }
126 232880 }
127
128 public:
129 453 StringDump() : current_block(0), block_pos(BLOCK_SIZE), block_size(BLOCK_SIZE) {}
130
131 453 ~StringDump() {
132 _RPT0(0, "StringDump: DeAllocating all stringblocks.\r\n");
133 453 char* p = current_block;
134
2/2
✓ Branch 6 → 3 taken 453 times.
✓ Branch 6 → 7 taken 453 times.
906 while (p) {
135 453 char* next = *(char**)p;
136
1/2
✓ Branch 3 → 4 taken 453 times.
✗ Branch 3 → 5 not taken.
453 delete[] p;
137 453 p = next;
138 }
139 453 }
140
141 // SaveString with len==-1 can store more than 'int' sized data
142 // up to size_t theoretically even on 32 bits.
143 // Public interface defines 'len' as int, this can we live with.
144 256437 char* SaveString(const char* s, int _len = -1, bool escape = false) {
145
2/2
✓ Branch 2 → 3 taken 36 times.
✓ Branch 2 → 4 taken 256401 times.
256437 size_t srclen = (_len == -1) ? strlen(s) : _len >= 0 ? static_cast<size_t>(_len) : 0;
146 size_t len;
147
148 256437 std::string ss;
149
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 39 taken 256437 times.
256437 if (escape) {
150 ss.reserve(srclen); // worst case. Note: input string may not be closed by 0x00
151 // PF: no need for WideString conversion, utf8 lower 128 ascii characters are freely searchable w/o conversion
152 len = 0;
153 for (size_t i = 0; s[i] && i<srclen; ++i, ++len) {
154 if (s[i] == '\\') {
155 switch (s[i + 1]) {
156 case 'n': ss += '\n'; ++i; continue;
157 case 'r': ss += '\r'; ++i; continue;
158 case 't': ss += '\t'; ++i; continue;
159 case '0': ss += '\0'; ++i; continue;
160 case 'a': ss += '\a'; ++i; continue;
161 case 'f': ss += '\f'; ++i; continue;
162 case '\\': ss += '\\'; ++i; continue;
163 case '\"': ss += '\"'; ++i; continue;
164 case '\'': ss += '\''; ++i; continue;
165 case 'b': ss += '\b'; ++i; continue;
166 case 'v': ss += '\v'; ++i; continue;
167 }
168 }
169 ss += s[i];
170 }
171 len = ss.size();
172 s = ss.c_str();
173 }
174 else {
175 256437 len = srclen;
176 }
177
178 // check in cache content
179
1/2
✓ Branch 40 → 41 taken 256437 times.
✗ Branch 40 → 50 not taken.
256437 const char* test_ptr = get_string(s, len);
180
2/2
✓ Branch 41 → 42 taken 23557 times.
✓ Branch 41 → 43 taken 232880 times.
256437 if(test_ptr != nullptr) // same string found in cache
181 23557 return const_cast<char *>(test_ptr);
182
183
1/2
✓ Branch 43 → 44 taken 232880 times.
✗ Branch 43 → 50 not taken.
232880 ensure_length(len);
184 232880 char* result = current_block + block_pos;
185 232880 memcpy(result, s, len);
186 232880 result[len] = 0;
187 232880 block_pos += AlignNumber(len + 1, sizeof(char*)); // Keep word-aligned
188
189
1/2
✓ Branch 45 → 46 taken 232880 times.
✗ Branch 45 → 50 not taken.
232880 add_string(result, len); // update cache
190
191 232880 return result;
192 256437 }
193
194 void Clear() {
195 if (current_block) {
196 // deallocate string blocks except the first one
197 while (char* p = *(char**)current_block) {
198 delete[] current_block;
199 current_block = p;
200 }
201 block_pos = sizeof(char*);
202 block_size = BLOCK_SIZE;
203 }
204 }
205 };
206
207 class VarFrame
208 {
209 typedef std::unordered_map<const char*, AVSValue, ihash_ascii, iequal_to_ascii> ValueMap;
210 ValueMap variables;
211
212 public:
213 2718 VarFrame() {
214
1/2
✓ Branch 3 → 4 taken 2718 times.
✗ Branch 3 → 5 not taken.
2717 variables.max_load_factor(0.8f);
215 2718 }
216
217 // This method will not modify the *val argument if it returns false.
218 7497 bool Get(const char* name, AVSValue *val) const
219 {
220
1/2
✓ Branch 2 → 3 taken 7497 times.
✗ Branch 2 → 12 not taken.
7497 ValueMap::const_iterator v = variables.find(name);
221
2/2
✓ Branch 5 → 6 taken 1824 times.
✓ Branch 5 → 9 taken 5673 times.
7497 if (v != variables.end())
222 {
223
1/2
✓ Branch 7 → 8 taken 1824 times.
✗ Branch 7 → 12 not taken.
1824 *val = v->second;
224 1824 return true;
225 }
226 5673 return false;
227 }
228
229 268196 bool Set(const char* name, const AVSValue& val)
230 {
231
2/4
✓ Branch 2 → 3 taken 268196 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 268196 times.
✗ Branch 3 → 9 not taken.
268196 std::pair<ValueMap::iterator, bool> ret = variables.insert(ValueMap::value_type(name, val));
232
1/2
✓ Branch 6 → 7 taken 268196 times.
✗ Branch 6 → 12 not taken.
268196 ret.first->second = val;
233 268196 return ret.second;
234 }
235
236 453 void Clear()
237 {
238 453 variables.clear();
239 453 }
240 };
241
242 class VarStringFrame : public VarFrame
243 {
244 StringDump string_dump;
245 public:
246 256437 char* SaveString(const char* s, int len = -1, bool escape = false) {
247 256437 return string_dump.SaveString(s, len, escape);
248 }
249
250 453 void Clear()
251 {
252 //string_dump.Clear(); // do not destroy string (it is destroyed at destructor)
253 453 VarFrame::Clear();
254 453 }
255 };
256
257 class ConcurrentVarStringFrame : protected VarStringFrame
258 {
259 // avoid write/read concurrency of global variables in runtime scripts in MT
260 mutable std::mutex var_mutex;
261
262 public:
263 // This method will not modify the *val argument if it returns false.
264 3745 bool Get(const char* name, AVSValue *val) const
265 {
266
1/2
✓ Branch 2 → 3 taken 3745 times.
✗ Branch 2 → 10 not taken.
3745 std::lock_guard<std::mutex> lock(var_mutex); // avoid concurrency for global variables
267
1/2
✓ Branch 3 → 4 taken 3745 times.
✗ Branch 3 → 8 not taken.
7490 return VarFrame::Get(name, val);
268 3745 }
269
270 268177 bool Set(const char* name, const AVSValue& val)
271 {
272
1/2
✓ Branch 2 → 3 taken 268177 times.
✗ Branch 2 → 10 not taken.
268177 std::lock_guard<std::mutex> lock(var_mutex); // avoid concurrency for global variables
273
1/2
✓ Branch 3 → 4 taken 268177 times.
✗ Branch 3 → 8 not taken.
536354 return VarFrame::Set(name, val);
274 268177 }
275
276 256437 char* SaveString(const char* s, int len = -1, bool escape = false) {
277
1/2
✓ Branch 2 → 3 taken 256437 times.
✗ Branch 2 → 10 not taken.
256437 std::lock_guard<std::mutex> lock(var_mutex); // avoid concurrency for global variables
278
1/2
✓ Branch 3 → 4 taken 256437 times.
✗ Branch 3 → 8 not taken.
512874 return VarStringFrame::SaveString(s, len, escape);
279 256437 }
280
281 453 void Clear()
282 {
283
1/2
✓ Branch 2 → 3 taken 453 times.
✗ Branch 2 → 6 not taken.
453 std::lock_guard<std::mutex> lock(var_mutex); // avoid concurrency for global variables
284 453 VarStringFrame::Clear();
285 453 }
286 };
287
288 class VarTable
289 {
290 private:
291 ConcurrentVarStringFrame* topFrame;
292
293 std::vector<std::unique_ptr<VarFrame>> stackFrames;
294 std::vector<std::unique_ptr<VarStringFrame>> globalFrames;
295
296 std::vector<std::unique_ptr<VarFrame>> stackPool;
297 std::vector<std::unique_ptr<VarStringFrame>> globalPool;
298
299 public:
300 2265 VarTable(ConcurrentVarStringFrame* topFrame) : topFrame(topFrame)
301 {
302
1/2
✓ Branch 6 → 7 taken 2265 times.
✗ Branch 6 → 8 not taken.
2265 Push();
303 2265 }
304
305 453 void Clear()
306 {
307 453 stackFrames.clear();
308 453 globalFrames.clear();
309 453 stackPool.clear();
310 453 globalPool.clear();
311 453 }
312
313 2265 void Push()
314 {
315
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 9 taken 2265 times.
2265 if (stackPool.size() > 0) {
316 stackFrames.emplace_back(std::move(stackPool.back()));
317 stackPool.pop_back();
318 }
319 else {
320
4/10
✓ Branch 9 → 10 taken 2265 times.
✗ Branch 9 → 19 not taken.
✓ Branch 10 → 11 taken 2265 times.
✗ Branch 10 → 16 not taken.
✓ Branch 11 → 12 taken 2263 times.
✗ Branch 11 → 16 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 2263 times.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
2265 stackFrames.emplace_back(new VarFrame());
321 }
322 2263 }
323
324 void Pop()
325 {
326 assert(stackFrames.size() > 0);
327 stackFrames.back()->Clear();
328 stackPool.emplace_back(std::move(stackFrames.back()));
329 stackFrames.pop_back();
330 }
331
332 void PushGlobal()
333 {
334 Push();
335 if (globalPool.size() > 0) {
336 globalFrames.emplace_back(std::move(globalPool.back()));
337 globalPool.pop_back();
338 }
339 else {
340 globalFrames.emplace_back(new VarStringFrame());
341 }
342 }
343
344 void PopGlobal()
345 {
346 Pop();
347 assert(globalFrames.size() > 0);
348 globalFrames.back()->Clear();
349 globalPool.emplace_back(std::move(globalFrames.back()));
350 globalFrames.pop_back();
351 }
352
353 19 bool Set(const char* name, const AVSValue& val)
354 {
355 19 return stackFrames.back()->Set(name, val);
356 }
357
358 256852 bool SetGlobal(const char* name, const AVSValue& val)
359 {
360
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 8 taken 256852 times.
256852 if (globalFrames.size() > 0) {
361 return globalFrames.back()->Set(name, val);
362 }
363 256852 return topFrame->Set(name, val);
364 }
365
366 3752 bool Get(const char* name, AVSValue *val) const
367 {
368
5/6
✓ Branch 3 → 4 taken 3752 times.
✗ Branch 3 → 9 not taken.
✓ Branch 7 → 8 taken 7 times.
✓ Branch 7 → 9 taken 3745 times.
✓ Branch 10 → 11 taken 7 times.
✓ Branch 10 → 12 taken 3745 times.
3752 if (stackFrames.size() > 0 && stackFrames.back()->Get(name, val)) {
369 7 return true;
370 }
371
2/4
✓ Branch 20 → 21 taken 3745 times.
✗ Branch 20 → 25 not taken.
✗ Branch 21 → 13 not taken.
✓ Branch 21 → 22 taken 3745 times.
3745 for (auto it = globalFrames.rbegin(); it != globalFrames.rend(); ++it) {
372 if ((**it).Get(name, val)) {
373 return true;
374 }
375 }
376 3745 return topFrame->Get(name, val);
377 }
378
379 256437 char* SaveString(const char* s, int len = -1, bool escape = false)
380 {
381
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 8 taken 256437 times.
256437 if (globalFrames.size() > 0) {
382 return globalFrames.back()->SaveString(s, len, escape);
383 }
384 256437 return topFrame->SaveString(s, len, escape);
385 }
386 };
387
388 #endif // AVSCORE_VARTABLE_H
389