1 #include "memorysearch.hpp"
4 #include "serialization.hpp"
8 memory_search::memory_search(memory_space
& space
) throw(std::bad_alloc
)
17 typedef uint8_t value_type
;
18 bool operator()(uint8_t oldv
, uint8_t newv
) const throw() { return true; }
25 search_value(T v
) throw() { val
= v
; }
26 bool operator()(T oldv
, T newv
) const throw() { return (newv
== val
); }
31 struct search_difference
34 search_difference(T v
) throw() { val
= v
; }
35 bool operator()(T oldv
, T newv
) const throw() { return ((newv
- oldv
) == val
); }
43 bool operator()(T oldv
, T newv
) const throw() { return (newv
< oldv
); }
50 bool operator()(T oldv
, T newv
) const throw() { return (newv
<= oldv
); }
57 bool operator()(T oldv
, T newv
) const throw() { return (newv
== oldv
); }
64 bool operator()(T oldv
, T newv
) const throw() { return (newv
!= oldv
); }
71 bool operator()(T oldv
, T newv
) const throw() { return (newv
>= oldv
); }
78 bool operator()(T oldv
, T newv
) const throw() { return (newv
> oldv
); }
85 bool operator()(T oldv
, T newv
) const throw()
87 T mask
= (T
)1 << (sizeof(T
) * 8 - 1);
89 return ((diff
& mask
) != (T
)0);
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);
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);
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);
131 struct search_value_helper
133 typedef typename
T::value_type value_type
;
134 search_value_helper(const T
& v
) throw()
138 bool operator()(const uint8_t* newv
, const uint8_t* oldv
, uint64_t left
, int endian
) const throw()
140 if(left
< sizeof(value_type
))
142 value_type v1
= read_of_endian
<value_type
>(oldv
, endian
);
143 value_type v2
= read_of_endian
<value_type
>(newv
, endian
);
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));
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));
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;
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())
185 unsigned char* mem
= region
.direct_map
;
186 uint64_t rsize
= region
.size
;
187 int endian
= region
.endian
;
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
) {
195 i
= next_multiple_of_64(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
);
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())
210 uint64_t rsize
= region
.size
;
211 int endian
= region
.endian
;
213 uint64_t switch_at
= ibase
+ rsize
- rbase
; //The smallest i not in this region.
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
) {
228 memmove(buffer
, buffer
+ buffer_offset
, buffer_remaining
);
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
) {
238 i
= next_multiple_of_64(i
);
239 uint64_t advance
= i
- old_i
;
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
);
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
)
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
);
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
)
289 uint64_t rsize
= region
.size
;
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
) {
297 i
= next_multiple_of_64(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);
313 uint64_t size
= previous_content
.size();
316 t
= mspace
.lookup_linear(i
);
319 dq_all_after(&still_in
[0], candidates
, size
, i
);
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);
333 uint64_t size
= previous_content
.size();
337 t
= mspace
.lookup_linear(i
);
340 dq_all_after(&still_in
[0], candidates
, size
, i
);
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
);
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()
375 auto t
= mspace
.lookup_linear(0);
378 //Search for linear range containing the address.
380 t
= mspace
.lookup_linear(i
);
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
)
391 memcpy(buf
, &previous_content
[linaddr
], maxr
);
392 return read_of_endian
<T
>(buf
, t
.first
->endian
);
394 i
+= t
.first
->size
- t
.second
;
400 template<typename T
> void memorysearch_pull_type(memory_search
& s
)
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
)
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()
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);
468 t
= mspace
.lookup_linear(i
);
471 candidates_block(out
, *t
.first
, t
.second
, i
, &still_in
[0], previous_content
.size());
472 i
+= t
.first
->size
- t
.second
;
477 bool memory_search::is_candidate(uint64_t addr
) throw()
479 auto t
= mspace
.lookup_linear(0);
485 t
= mspace
.lookup_linear(i
);
488 if(i
>= previous_content
.size())
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
;
503 uint64_t memory_search::cycle_candidate_vma(uint64_t addr
, bool next
) throw()
505 auto t
= mspace
.lookup_linear(0);
511 t
= mspace
.lookup_linear(i
);
514 if(i
>= previous_content
.size())
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
;
528 while(tryoff
< finoff
|| !warped
) {
529 if(tryoff
>= switch_at
) {
535 if(still_in
[tryoff
/ 64] == 0)
536 tryoff
= next_multiple_of_64(tryoff
);
538 if((still_in
[tryoff
/ 64] >> (tryoff
% 64)) & 1)
539 return tryoff
- i
+ baseaddr
;
546 while(tryoff
> finoff
|| !warped
) {
548 tryoff
= switch_at
- 1;
553 if(still_in
[tryoff
/ 64] == 0)
554 tryoff
= prev_multiple_of_64(tryoff
);
556 if((still_in
[tryoff
/ 64] >> (tryoff
% 64)) & 1)
557 return tryoff
- i
+ baseaddr
;
563 i
+= t
.first
->size
- t
.second
;
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
;
576 still_in
[linearram
/ 64] = (1ULL << (linearram
% 64)) - 1;
577 candidates
= linearram
;
579 auto t
= mspace
.lookup_linear(0);
585 t
= mspace
.lookup_linear(i
);
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
);
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
602 uint64_t linsize
= mspace
.get_linear_size();
603 if(type
== ST_PREVMEM
)
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;
610 throw std::runtime_error("Invalid savestate type");
613 write64ube(&buffer
[1], linsize
);
615 if(type
== ST_PREVMEM
|| type
== ST_ALL
) {
616 memcpy(&buffer
[offset
], &previous_content
[0], min(linsize
, (uint64_t)previous_content
.size()));
619 if(type
== ST_SET
|| type
== ST_ALL
) {
620 write64ube(&buffer
[offset
], candidates
);
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
]);
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())
639 savestate_type type
= (savestate_type
)buffer
[0];
641 if(type
== ST_PREVMEM
|| type
== ST_ALL
) {
642 memcpy(&previous_content
[0], &buffer
[offset
], min(linsize
, (uint64_t)previous_content
.size()));
645 if(type
== ST_SET
|| type
== ST_ALL
) {
646 candidates
= read64ube(&buffer
[offset
]);
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
]);