public PatternScanInstructionSet MakeInstructionSetWithoutMaskLong() { return(PatternScanInstructionSet.FromStringPattern("DA 69 DD AA FE B9 BB CC DD EE FF")); }
/// <summary> /// Attempts to find a given pattern inside the memory region this class was created with. /// This method generates a list of instructions, which more efficiently determine at any array index if pattern is found. /// This method generally works better when the expected offset is bigger than 4096. /// </summary> /// <param name="pattern"> /// The pattern to look for inside the given region. /// Example: "11 22 33 ?? 55". /// Key: ?? represents a byte that should be ignored, anything else if a hex byte. i.e. 11 represents 0x11, 1F represents 0x1F /// </param> /// <returns>A result indicating an offset (if found) of the pattern.</returns> public PatternScanResult CompiledFindPattern(string pattern) { var instructionSet = PatternScanInstructionSet.FromStringPattern(pattern); int numberOfInstructions = instructionSet.NumberOfInstructions; int dataLength = _data.Length; byte *dataBasePointer = _dataPtr; byte *currentDataPointer; int lastIndex = dataLength - Math.Max(instructionSet.Length, sizeof(long)) + 1; // Note: All of this has to be manually inlined otherwise performance suffers, this is a bit ugly though :/ fixed(GenericInstruction *instructions = instructionSet.Instructions) { var firstInstruction = instructions[0]; /* * There is an optimization going on in here apart from manual inlining which is why * this function is a tiny mess of more goto statements than it seems necessary. * * Basically, it is considerably faster to reference a variable on the stack, than it is on the heap. * * This is because the compiler can address the stack bound variable relative to the current stack pointer, * as opposing to having to dereference a pointer and then take an offset from the result address. * * This ends up being considerably faster, which is important in a scenario where we are entirely CPU bound. */ int x = 0; while (x < lastIndex) { currentDataPointer = dataBasePointer + x; var compareValue = *(ulong *)currentDataPointer & firstInstruction.Mask; if (compareValue != firstInstruction.LongValue) { goto singleInstructionLoopExit; } if (numberOfInstructions <= 1) { return(new PatternScanResult(x)); } /* When NumberOfInstructions > 1 */ currentDataPointer += sizeof(ulong); int y = 1; do { compareValue = *(ulong *)currentDataPointer & instructions[y].Mask; if (compareValue != instructions[y].LongValue) { goto singleInstructionLoopExit; } currentDataPointer += sizeof(ulong); y++; }while (y < numberOfInstructions); return(new PatternScanResult(x)); singleInstructionLoopExit :; x++; } // Check last few bytes in cases pattern was not found and long overflows into possibly unallocated memory. return(SimpleFindPattern(pattern, lastIndex)); // PS. This function is a prime example why the `goto` statement is frowned upon. // I have to use it here for performance though. } }
public PatternScanInstructionSet MakeInstructionSetWithoutMask() { return(PatternScanInstructionSet.FromStringPattern("DA 69 DD AA FE B9")); }