| 12031 | } |
| 12032 | |
| 12033 | pcret *get_compiled_regex(LPTSTR aRegEx, pcret_extra *&aExtra, int *aOptionsLength, ResultToken *aResultToken) |
| 12034 | // Returns the compiled RegEx, or NULL on failure. |
| 12035 | // This function is called by things other than built-in functions so it should be kept general-purpose. |
| 12036 | // Upon failure, if aResultToken!=NULL: |
| 12037 | // - An exception is thrown with a descriptive message on failure. |
| 12038 | // - *aResultToken is set up to contain an empty string. |
| 12039 | // Upon success, the following output parameters are set based on the options that were specified: |
| 12040 | // aGetPositionsNotSubstrings |
| 12041 | // aExtra |
| 12042 | // L14: aOptionsLength is used by callouts to adjust cb->pattern_position to be relative to beginning of actual user-specified NeedleRegEx instead of string seen by PCRE. |
| 12043 | { |
| 12044 | if (!pcret_callout) |
| 12045 | { // Ensure this is initialized, even for ::RegExMatch() (to allow (?C) in window title regexes). |
| 12046 | pcret_callout = &RegExCallout; |
| 12047 | } |
| 12048 | |
| 12049 | // While reading from or writing to the cache, don't allow another thread entry. This is because |
| 12050 | // that thread (or this one) might write to the cache while the other one is reading/writing, which |
| 12051 | // could cause loss of data integrity (the hook thread can enter here via #HotIf WinActive/Exist & SetTitleMatchMode RegEx). |
| 12052 | // Together, Enter/LeaveCriticalSection reduce performance by only 1.4% in the tightest possible script |
| 12053 | // loop that hits the first cache entry every time. So that's the worst case except when there's an actual |
| 12054 | // collision, in which case performance suffers more because internally, EnterCriticalSection() does a |
| 12055 | // wait/semaphore operation, which is more costly. |
| 12056 | // Finally, the code size of all critical-section features together is less than 512 bytes (uncompressed), |
| 12057 | // so like performance, that's not a concern either. |
| 12058 | EnterCriticalSection(&g_CriticalRegExCache); // Request ownership of the critical section. If another thread already owns it, this thread will block until the other thread finishes. |
| 12059 | |
| 12060 | // SET UP THE CACHE. |
| 12061 | // This is a very crude cache for linear search. Of course, hashing would be better in the sense that it |
| 12062 | // would allow the cache to get much larger while still being fast (I believe PHP caches up to 4096 items). |
| 12063 | // Binary search might not be such a good idea in this case due to the time required to find the right spot |
| 12064 | // to insert a new cache item (however, items aren't inserted often, so it might perform quite well until |
| 12065 | // the cache contained thousands of RegEx's, which is unlikely to ever happen in most scripts). |
| 12066 | struct pcre_cache_entry |
| 12067 | { |
| 12068 | // For simplicity (and thus performance), the entire RegEx pattern including its options is cached |
| 12069 | // is stored in re_raw and that entire string becomes the RegEx's unique identifier for the purpose |
| 12070 | // of finding an entry in the cache. Technically, this isn't optimal because some options like Study |
| 12071 | // and aGetPositionsNotSubstrings don't alter the nature of the compiled RegEx. However, the CPU time |
| 12072 | // required to strip off some options prior to doing a cache search seems likely to offset much of the |
| 12073 | // cache's benefit. So for this reason, as well as rarity and code size issues, this policy seems best. |
| 12074 | LPTSTR re_raw; // The RegEx's literal string pattern such as "abc.*123". |
| 12075 | pcret *re_compiled; // The RegEx in compiled form. |
| 12076 | pcret_extra *extra; // NULL unless a study() was done (and NULL even then if study() didn't find anything). |
| 12077 | // int pcre_options; // Not currently needed in the cache since options are implicitly inside re_compiled. |
| 12078 | int options_length; // Lexikos: See aOptionsLength comment at beginning of this function. |
| 12079 | }; |
| 12080 | |
| 12081 | #define PCRE_CACHE_SIZE 100 // Going too high would be counterproductive due to the slowness of linear search (and also the memory utilization of so many compiled RegEx's). |
| 12082 | static pcre_cache_entry sCache[PCRE_CACHE_SIZE] = {{0}}; |
| 12083 | static int sLastInsert, sLastFound = -1; // -1 indicates "cache empty". |
| 12084 | int insert_pos; // v1.0.45.03: This is used to avoid updating sLastInsert until an insert actually occurs (it might not occur if a compile error occurs in the regex, or something else stops it early). |
| 12085 | |
| 12086 | // CHECK IF THIS REGEX IS ALREADY IN THE CACHE. |
| 12087 | if (sLastFound == -1) // Cache is empty, so insert this RegEx at the first position. |
| 12088 | insert_pos = 0; // A section further below will change sLastFound to be 0. |
| 12089 | else |
| 12090 | { |
no test coverage detected