Upload UI
[lsnes.git] / src / library / memorysearch.cpp
blob278f92e280aeedecc00f3a7fee5af6d2536cb15f
1 #include "memorysearch.hpp"
2 #include "eatarg.hpp"
3 #include "minmax.hpp"
4 #include "serialization.hpp"
5 #include "int24.hpp"
6 #include <iostream>
8 memory_search::memory_search(memory_space& space) throw(std::bad_alloc)
9 : mspace(space)
11 candidates = 0;
15 struct search_update
17 typedef uint8_t value_type;
18 bool operator()(uint8_t oldv, uint8_t newv) const throw() { return true; }
21 template<typename T>
22 struct search_value
24 typedef T value_type;
25 search_value(T v) throw() { val = v; }
26 bool operator()(T oldv, T newv) const throw() { return (newv == val); }
27 T val;
30 template<typename T>
31 struct search_difference
33 typedef T value_type;
34 search_difference(T v) throw() { val = v; }
35 bool operator()(T oldv, T newv) const throw() { return ((newv - oldv) == val); }
36 T val;
39 template<typename T>
40 struct search_lt
42 typedef T value_type;
43 bool operator()(T oldv, T newv) const throw() { return (newv < oldv); }
46 template<typename T>
47 struct search_le
49 typedef T value_type;
50 bool operator()(T oldv, T newv) const throw() { return (newv <= oldv); }
53 template<typename T>
54 struct search_eq
56 typedef T value_type;
57 bool operator()(T oldv, T newv) const throw() { return (newv == oldv); }
60 template<typename T>
61 struct search_ne
63 typedef T value_type;
64 bool operator()(T oldv, T newv) const throw() { return (newv != oldv); }
67 template<typename T>
68 struct search_ge
70 typedef T value_type;
71 bool operator()(T oldv, T newv) const throw() { return (newv >= oldv); }
74 template<typename T>
75 struct search_gt
77 typedef T value_type;
78 bool operator()(T oldv, T newv) const throw() { return (newv > oldv); }
81 template<typename T>
82 struct search_seqlt
84 typedef T value_type;
85 bool operator()(T oldv, T newv) const throw()
87 T mask = (T)1 << (sizeof(T) * 8 - 1);
88 T diff = newv - oldv;
89 return ((diff & mask) != (T)0);
93 template<typename T>
94 struct search_seqle
96 typedef T value_type;
97 bool operator()(T oldv, T newv) const throw()
99 T mask = (T)1 << (sizeof(T) * 8 - 1);
100 T diff = newv - oldv;
101 return ((diff & mask) != (T)0) || (diff == (T)0);
105 template<typename T>
106 struct search_seqge
108 typedef T value_type;
109 bool operator()(T oldv, T newv) const throw()
111 T mask = (T)1 << (sizeof(T) * 8 - 1);
112 T diff = newv - oldv;
113 return ((diff & mask) == (T)0);
117 template<typename T>
118 struct search_seqgt
120 typedef T value_type;
121 bool operator()(T oldv, T newv) const throw()
123 T mask = (T)1 << (sizeof(T) * 8 - 1);
124 T diff = newv - oldv;
125 return ((diff & mask) == (T)0) && (diff != (T)0);
130 template<typename T>
131 struct search_value_helper
133 typedef typename T::value_type value_type;
134 search_value_helper(const T& v) throw()
135 : val(v)
138 bool operator()(const uint8_t* newv, const uint8_t* oldv, uint64_t left, int endian) const throw()
140 if(left < sizeof(value_type))
141 return false;
142 value_type v1 = read_of_endian<value_type>(oldv, endian);
143 value_type v2 = read_of_endian<value_type>(newv, endian);
144 return val(v1, v2);
146 const T& val;
149 namespace
151 void dq_all_after(uint64_t* still_in, uint64_t& candidates, uint64_t size, uint64_t after)
153 for(uint64_t i = after; i < size; i++) {
154 if((still_in[i / 64] >> (i % 64)) & 1) {
155 still_in[i / 64] &= ~(1ULL << (i % 64));
156 candidates--;
161 inline void dq_entry(uint64_t* still_in, uint64_t& candidates, uint64_t i)
163 if((still_in[i / 64] >> (i % 64)) & 1) {
164 still_in[i / 64] &= ~(1ULL << (i % 64));
165 candidates--;
169 inline uint64_t next_multiple_of_64(uint64_t i)
171 return (i + 64) >> 6 << 6;
174 inline uint64_t prev_multiple_of_64(uint64_t i)
176 return ((i - 64) >> 6 << 6) + 63;
179 template<typename T>
180 void search_block_mapped(uint64_t* still_in, uint64_t& candidates, memory_region& region, uint64_t rbase,
181 uint64_t ibase, T& helper, std::vector<uint8_t>& previous_content)
183 if(ibase >= previous_content.size())
184 return;
185 unsigned char* mem = region.direct_map;
186 uint64_t rsize = region.size;
187 int endian = region.endian;
188 uint64_t i = ibase;
189 uint64_t switch_at = ibase + rsize - rbase; //The smallest i not in this region.
190 rsize = min(rsize, previous_content.size() - ibase);
191 for(unsigned j = rbase; j < rsize; i++, j++) {
192 //Advance blocks of 64 addresses if none of the addresses match.
193 while(still_in[i / 64] == 0 && next_multiple_of_64(i) <= switch_at) {
194 uint64_t old_i = i;
195 i = next_multiple_of_64(i);
196 j += i - old_i;
198 //This might match. Check it.
199 if(!helper(mem + j, &previous_content[i], rsize - j, endian))
200 dq_entry(still_in, candidates, i);
204 template<typename T>
205 void search_block_read(uint64_t* still_in, uint64_t& candidates, memory_region& region, uint64_t rbase,
206 uint64_t ibase, T& helper, std::vector<uint8_t>& previous_content)
208 if(ibase >= previous_content.size())
209 return;
210 uint64_t rsize = region.size;
211 int endian = region.endian;
212 uint64_t i = ibase;
213 uint64_t switch_at = ibase + rsize - rbase; //The smallest i not in this region.
215 //The buffer.
216 const size_t buffer_capacity = 4096;
217 unsigned char buffer[buffer_capacity];
218 uint64_t buffer_offset = 0; //The offset in buffer.
219 uint64_t buffer_remaining = 0; //Number of bytes remaining in buffer.
220 uint64_t buffer_soffset = rbase; //The offset buffer start corresponds to.
221 uint64_t buffer_eoffset = rbase; //The first offset not in buffer.
223 rsize = min(rsize, previous_content.size() - ibase);
224 for(unsigned j = rbase; j < rsize; i++, j++) {
225 //Fill the buffer again if it has gotten low enough.
226 if(buffer_remaining < 256 && buffer_eoffset != rsize) {
227 if(buffer_remaining)
228 memmove(buffer, buffer + buffer_offset, buffer_remaining);
229 buffer_offset = 0;
230 size_t fill_amount = min((uint64_t)buffer_capacity, rsize - buffer_eoffset);
231 region.read(buffer_eoffset, buffer + buffer_remaining, fill_amount);
232 buffer_eoffset += fill_amount;
233 buffer_remaining += fill_amount;
235 //Advance blocks of 64 addresses if none of the addresses match.
236 while(still_in[i / 64] == 0 && next_multiple_of_64(i) <= switch_at) {
237 uint64_t old_i = i;
238 i = next_multiple_of_64(i);
239 uint64_t advance = i - old_i;
240 j += advance;
241 buffer_offset += advance;
242 buffer_remaining -= advance;
243 buffer_soffset += advance;
245 //This might match. Check it.
246 if(!helper(buffer + buffer_offset, &previous_content[i], buffer_remaining, endian))
247 dq_entry(still_in, candidates, i);
248 buffer_offset++;
249 buffer_remaining--;
250 buffer_soffset++;
254 void copy_block_mapped(uint8_t* old, memory_region& region, uint64_t rbase, uint64_t maxr)
256 memcpy(old, region.direct_map + rbase, min(region.size - rbase, maxr));
259 void copy_block_read(uint8_t* old, memory_region& region, uint64_t rbase, uint64_t maxr)
261 region.read(rbase, old, min(region.size - rbase, maxr));
264 void dq_block(uint64_t* still_in, uint64_t& candidates, memory_region& region, uint64_t rbase, uint64_t ibase,
265 uint64_t first, uint64_t last, uint64_t ramsize)
267 if(ibase >= ramsize)
268 return;
269 if(last < region.base + rbase || first >= region.base + region.size)
270 return; //Nothing to DQ in this block.
271 first = max(first, region.base + rbase) - region.base + rbase;
272 last = last - region.base - rbase;
273 last = min(last, ramsize - ibase);
274 uint64_t ilast = ibase + last;
275 for(uint64_t i = ibase + first; i <= ilast; i++) {
276 while(still_in[i / 64] == 0 && next_multiple_of_64(i) <= ilast) {
277 i = next_multiple_of_64(i);
278 continue;
280 dq_entry(still_in, candidates, i);
284 void candidates_block(std::list<uint64_t>& out, memory_region& region, uint64_t rbase, uint64_t ibase,
285 uint64_t* still_in, uint64_t ramsize)
287 if(ibase >= ramsize)
288 return;
289 uint64_t rsize = region.size;
290 uint64_t i = ibase;
291 uint64_t switch_at = ibase + rsize - rbase; //The smallest i not in this region.
292 rsize = min(rsize, ramsize - ibase);
293 for(unsigned j = rbase; j < rsize; i++, j++) {
294 //Advance blocks of 64 addresses if none of the addresses match.
295 while(still_in[i / 64] == 0 && next_multiple_of_64(i) <= switch_at) {
296 uint64_t old_i = i;
297 i = next_multiple_of_64(i);
298 j += i - old_i;
300 if((still_in[i / 64] >> (i % 64)) & 1)
301 out.push_back(region.base + j);
307 void memory_search::dq_range(uint64_t first, uint64_t last)
309 auto t = mspace.lookup_linear(0);
310 if(!t.first)
311 return;
312 uint64_t i = 0;
313 uint64_t size = previous_content.size();
314 while(true) {
315 //Switch blocks.
316 t = mspace.lookup_linear(i);
317 if(!t.first) {
318 //DQ all rest.
319 dq_all_after(&still_in[0], candidates, size, i);
320 break;
322 dq_block(&still_in[0], candidates, *t.first, t.second, i, first, last, previous_content.size());
323 i += t.first->size - t.second;
327 template<class T> void memory_search::search(const T& obj) throw()
329 search_value_helper<T> helper(obj);
330 auto t = mspace.lookup_linear(0);
331 if(!t.first)
332 return;
333 uint64_t size = previous_content.size();
334 uint64_t i = 0;
335 while(true) {
336 //Switch blocks.
337 t = mspace.lookup_linear(i);
338 if(!t.first) {
339 //DQ all rest.
340 dq_all_after(&still_in[0], candidates, size, i);
341 break;
343 if(t.first->direct_map) {
344 search_block_mapped(&still_in[0], candidates, *t.first, t.second, i, helper, previous_content);
345 copy_block_mapped(&previous_content[i], *t.first, t.second,
346 max((uint64_t)previous_content.size(), i) - i);
347 } else {
348 search_block_read(&still_in[0], candidates, *t.first, t.second, i, helper, previous_content);
349 copy_block_read(&previous_content[i], *t.first, t.second,
350 max((uint64_t)previous_content.size(), i) - i);
352 i += t.first->size - t.second;
356 template<typename T> void memory_search::s_value(T value) throw() { search(search_value<T>(value)); }
357 template<typename T> void memory_search::s_difference(T value) throw() { search(search_difference<T>(value)); }
358 template<typename T> void memory_search::s_lt() throw() { search(search_lt<T>()); }
359 template<typename T> void memory_search::s_le() throw() { search(search_le<T>()); }
360 template<typename T> void memory_search::s_eq() throw() { search(search_eq<T>()); }
361 template<typename T> void memory_search::s_ne() throw() { search(search_ne<T>()); }
362 template<typename T> void memory_search::s_ge() throw() { search(search_ge<T>()); }
363 template<typename T> void memory_search::s_gt() throw() { search(search_gt<T>()); }
364 template<typename T> void memory_search::s_seqlt() throw() { search(search_seqlt<T>()); }
365 template<typename T> void memory_search::s_seqle() throw() { search(search_seqle<T>()); }
366 template<typename T> void memory_search::s_seqge() throw() { search(search_seqge<T>()); }
367 template<typename T> void memory_search::s_seqgt() throw() { search(search_seqgt<T>()); }
369 template<typename T> T memory_search::v_read(uint64_t addr) throw() { return mspace.read<T>(addr); }
370 template<typename T> void memory_search::v_write(uint64_t addr, T val) throw() { mspace.write<T>(addr, val); }
372 template<typename T> T memory_search::v_readold(uint64_t addr) throw()
374 uint64_t i = 0;
375 auto t = mspace.lookup_linear(0);
376 if(!t.first)
377 return 0;
378 //Search for linear range containing the address.
379 while(true) {
380 t = mspace.lookup_linear(i);
381 if(!t.first)
382 return 0;
383 if(t.first->base <= addr && t.first->base + t.first->size > addr) {
384 //Global address t.first->base <=> linear address i.
385 uint64_t linaddr = addr - t.first->base + i;
386 uint64_t maxr = t.first->size + t.first->base - addr;
387 maxr = min(maxr, (uint64_t)sizeof(T));
388 char buf[sizeof(T)] = {0};
389 if(previous_content.size() < linaddr + maxr)
390 return 0;
391 memcpy(buf, &previous_content[linaddr], maxr);
392 return read_of_endian<T>(buf, t.first->endian);
394 i += t.first->size - t.second;
397 return 0;
400 template<typename T> void memorysearch_pull_type(memory_search& s)
402 T val;
403 eat_argument(&memory_search::s_value<T>);
404 eat_argument(&memory_search::s_difference<T>);
405 eat_argument(&memory_search::s_lt<T>);
406 eat_argument(&memory_search::s_le<T>);
407 eat_argument(&memory_search::s_eq<T>);
408 eat_argument(&memory_search::s_ne<T>);
409 eat_argument(&memory_search::s_ge<T>);
410 eat_argument(&memory_search::s_gt<T>);
411 eat_argument(&memory_search::s_seqlt<T>);
412 eat_argument(&memory_search::s_seqle<T>);
413 eat_argument(&memory_search::s_seqge<T>);
414 eat_argument(&memory_search::s_seqgt<T>);
415 eat_argument(&memory_search::v_read<T>);
416 eat_argument(&memory_search::v_readold<T>);
417 eat_argument(&memory_search::v_write<T>);
420 template<typename T> void memorysearch_pull_type2(memory_search& s)
422 T val;
423 eat_argument(&memory_search::s_value<T>);
424 eat_argument(&memory_search::s_difference<T>);
425 eat_argument(&memory_search::s_lt<T>);
426 eat_argument(&memory_search::s_le<T>);
427 eat_argument(&memory_search::s_eq<T>);
428 eat_argument(&memory_search::s_ne<T>);
429 eat_argument(&memory_search::s_ge<T>);
430 eat_argument(&memory_search::s_gt<T>);
431 eat_argument(&memory_search::v_read<T>);
432 eat_argument(&memory_search::v_readold<T>);
433 eat_argument(&memory_search::v_write<T>);
436 void memorysearch_pull_all(memory_search& s)
438 memorysearch_pull_type<int8_t>(s);
439 memorysearch_pull_type<uint8_t>(s);
440 memorysearch_pull_type<int16_t>(s);
441 memorysearch_pull_type<uint16_t>(s);
442 memorysearch_pull_type<ss_int24_t>(s);
443 memorysearch_pull_type<ss_uint24_t>(s);
444 memorysearch_pull_type<int32_t>(s);
445 memorysearch_pull_type<uint32_t>(s);
446 memorysearch_pull_type<int64_t>(s);
447 memorysearch_pull_type<uint64_t>(s);
448 memorysearch_pull_type2<float>(s);
449 memorysearch_pull_type2<double>(s);
452 void memory_search::update() throw() { search(search_update()); }
454 uint64_t memory_search::get_candidate_count() throw()
456 return candidates;
459 std::list<uint64_t> memory_search::get_candidates() throw(std::bad_alloc)
461 std::list<uint64_t> out;
462 auto t = mspace.lookup_linear(0);
463 if(!t.first)
464 return out;
465 uint64_t i = 0;
466 while(true) {
467 //Switch blocks.
468 t = mspace.lookup_linear(i);
469 if(!t.first)
470 return out;
471 candidates_block(out, *t.first, t.second, i, &still_in[0], previous_content.size());
472 i += t.first->size - t.second;
474 return out;
477 bool memory_search::is_candidate(uint64_t addr) throw()
479 auto t = mspace.lookup_linear(0);
480 if(!t.first)
481 return false;
482 uint64_t i = 0;
483 while(true) {
484 //Switch blocks.
485 t = mspace.lookup_linear(i);
486 if(!t.first)
487 return false;
488 if(i >= previous_content.size())
489 return false;
490 uint64_t rsize = t.first->size;
491 uint64_t switch_at = i + rsize - t.second; //The smallest i not in this region.
492 rsize = min(rsize, previous_content.size() - i);
493 if(addr >= t.first->base + t.second && addr < t.first->base + rsize) {
494 uint64_t adv = addr - (t.first->base + t.second);
495 uint64_t ix = i + adv;
496 return ((still_in[ix / 64] >> (ix % 64)) & 1);
498 i += t.first->size - t.second;
500 return false;
503 uint64_t memory_search::cycle_candidate_vma(uint64_t addr, bool next) throw()
505 auto t = mspace.lookup_linear(0);
506 if(!t.first)
507 return false;
508 uint64_t i = 0;
509 while(true) {
510 //Switch blocks.
511 t = mspace.lookup_linear(i);
512 if(!t.first)
513 return addr;
514 if(i >= previous_content.size())
515 return addr;
516 uint64_t rsize = t.first->size;
517 uint64_t switch_at = i + rsize - t.second; //The smallest i not in this region.
518 rsize = min(rsize, previous_content.size() - i);
519 if(addr >= t.first->base + t.second && addr < t.first->base + rsize) {
520 uint64_t baseaddr = t.first->base + t.second;
521 int64_t tryoff = addr - baseaddr + i;
522 uint64_t finoff = tryoff;
523 uint64_t warp = i;
524 bool warped = false;
525 if(next) {
526 //Cycle forwards.
527 tryoff++;
528 while(tryoff < finoff || !warped) {
529 if(tryoff >= switch_at) {
530 tryoff = warp;
531 if(warped)
532 return addr;
533 warped = true;
535 if(still_in[tryoff / 64] == 0)
536 tryoff = next_multiple_of_64(tryoff);
537 else {
538 if((still_in[tryoff / 64] >> (tryoff % 64)) & 1)
539 return tryoff - i + baseaddr;
540 tryoff++;
543 } else {
544 //Cycle backwards.
545 tryoff--;
546 while(tryoff > finoff || !warped) {
547 if(tryoff < warp) {
548 tryoff = switch_at - 1;
549 if(warped)
550 return addr;
551 warped = true;
553 if(still_in[tryoff / 64] == 0)
554 tryoff = prev_multiple_of_64(tryoff);
555 else {
556 if((still_in[tryoff / 64] >> (tryoff % 64)) & 1)
557 return tryoff - i + baseaddr;
558 tryoff--;
563 i += t.first->size - t.second;
565 return addr;
568 void memory_search::reset() throw(std::bad_alloc)
570 uint64_t linearram = mspace.get_linear_size();
571 previous_content.resize(linearram);
572 still_in.resize((linearram + 63) / 64);
573 for(uint64_t i = 0; i < linearram / 64; i++)
574 still_in[i] = 0xFFFFFFFFFFFFFFFFULL;
575 if(linearram % 64)
576 still_in[linearram / 64] = (1ULL << (linearram % 64)) - 1;
577 candidates = linearram;
579 auto t = mspace.lookup_linear(0);
580 if(!t.first)
581 return;
582 uint64_t i = 0;
583 while(true) {
584 //Switch blocks.
585 t = mspace.lookup_linear(i);
586 if(!t.first)
587 break;
588 if(t.first->direct_map)
589 copy_block_mapped(&previous_content[i], *t.first, t.second,
590 max((uint64_t)previous_content.size(), i) - i);
591 else
592 copy_block_read(&previous_content[i], *t.first, t.second,
593 max((uint64_t)previous_content.size(), i) - i);
594 i += t.first->size - t.second;
599 void memory_search::savestate(std::vector<char>& buffer, enum savestate_type type) const
601 size_t size;
602 uint64_t linsize = mspace.get_linear_size();
603 if(type == ST_PREVMEM)
604 size = 9 + linsize;
605 else if(type == ST_SET)
606 size = 17 + (linsize + 63) / 64 * 8;
607 else if(type == ST_ALL)
608 size = 17 + linsize + (linsize + 63) / 64 * 8;
609 else
610 throw std::runtime_error("Invalid savestate type");
611 buffer.resize(size);
612 buffer[0] = type;
613 write64ube(&buffer[1], linsize);
614 size_t offset = 9;
615 if(type == ST_PREVMEM || type == ST_ALL) {
616 memcpy(&buffer[offset], &previous_content[0], min(linsize, (uint64_t)previous_content.size()));
617 offset += linsize;
619 if(type == ST_SET || type == ST_ALL) {
620 write64ube(&buffer[offset], candidates);
621 offset += 8;
622 size_t bound = min((linsize + 63) / 64, (uint64_t)still_in.size());
623 for(unsigned i = 0; i < bound; i++) {
624 write64ube(&buffer[offset], still_in[i]);
625 offset += 8;
630 void memory_search::loadstate(const std::vector<char>& buffer)
632 if(buffer.size() < 9 || buffer[0] < ST_PREVMEM || buffer[0] > ST_ALL)
633 throw std::runtime_error("Invalid memory search save");
634 uint64_t linsize = read64ube(&buffer[1]);
635 if(linsize != mspace.get_linear_size())
636 throw std::runtime_error("Save size mismatch (not from this game)");
637 if(!previous_content.size())
638 reset();
639 savestate_type type = (savestate_type)buffer[0];
640 size_t offset = 9;
641 if(type == ST_PREVMEM || type == ST_ALL) {
642 memcpy(&previous_content[0], &buffer[offset], min(linsize, (uint64_t)previous_content.size()));
643 offset += linsize;
645 if(type == ST_SET || type == ST_ALL) {
646 candidates = read64ube(&buffer[offset]);
647 offset += 8;
648 size_t bound = min((linsize + 63) / 64, (uint64_t)still_in.size());
649 for(unsigned i = 0; i < bound; i++) {
650 still_in[i] = read64ube(&buffer[offset]);
651 offset += 8;