< prev index next >

src/hotspot/share/utilities/stringUtils.cpp

Print this page
*** 23,10 ***
--- 23,11 ---
   */
  
  #include "precompiled.hpp"
  #include "jvm_io.h"
  #include "utilities/debug.hpp"
+ #include "utilities/ostream.hpp"
  #include "utilities/stringUtils.hpp"
  
  #include <ctype.h>
  #include <string.h>
  

*** 68,10 ***
--- 69,225 ---
    }
  
    return 2.0 * (double) hit / (double) total;
  }
  
+ class StringMatcher {
+  public:
+   typedef int getc_function_t(const char* &source, const char* limit);
+ 
+  private:
+   // These do not get properly inlined.
+   // For full performance, this should be a template class
+   // parameterized by two function arguments.
+   getc_function_t* _pattern_getc;
+   getc_function_t* _string_getc;
+ 
+  public:
+   StringMatcher(getc_function_t pattern_getc,
+                 getc_function_t string_getc)
+     : _pattern_getc(pattern_getc),
+       _string_getc(string_getc)
+   { }
+ 
+   enum {  // special results from _pattern_getc
+     string_match_comma  = -0x100 + ',',
+     string_match_star   = -0x100 + '*',
+     string_match_eos    = -0x100 + '\0'
+   };
+ 
+  private:
+   const char*
+   skip_anchor_word(const char* match,
+                    const char* match_end,
+                    int anchor_length,
+                    const char* pattern,
+                    const char* pattern_end) {
+     assert(pattern < pattern_end && anchor_length > 0, "");
+     const char* begp = pattern;
+     int ch1 = _pattern_getc(begp, pattern_end);
+     // note that begp is now advanced over ch1
+     assert(ch1 > 0, "regular char only");
+     const char* matchp = match;
+     const char* limitp = match_end - anchor_length;
+     while (matchp <= limitp) {
+       int mch = _string_getc(matchp, match_end);
+       if (mch == ch1) {
+         const char* patp = begp;
+         const char* anchorp = matchp;
+         while (patp < pattern_end) {
+           char ch = _pattern_getc(patp, pattern_end);
+           char mch = _string_getc(anchorp, match_end);
+           if (mch != ch) {
+             anchorp = nullptr;
+             break;
+           }
+         }
+         if (anchorp != nullptr) {
+           return anchorp;  // Found a full copy of the anchor.
+         }
+         // That did not work, so restart the search for ch1.
+       }
+     }
+     return nullptr;
+   }
+ 
+  public:
+   bool string_match(const char* pattern,
+                     const char* string) {
+     return string_match(pattern, pattern + strlen(pattern),
+                         string, string + strlen(string));
+   }
+   bool string_match(const char* pattern, const char* pattern_end,
+                     const char* string, const char* string_end) {
+     const char* patp = pattern;
+     switch (_pattern_getc(patp, pattern_end)) {
+     case string_match_eos:
+       return false;  // Empty pattern is always false.
+     case string_match_star:
+       if (patp == pattern_end) {
+         return true;   // Lone star pattern is always true.
+       }
+       break;
+     }
+     patp = pattern;  // Reset after lookahead.
+     const char* matchp = string;  // nullptr if failing
+     for (;;) {
+       int ch = _pattern_getc(patp, pattern_end);
+       switch (ch) {
+       case string_match_eos:
+       case string_match_comma:
+         // End of a list item; see if it's a match.
+         if (matchp == string_end) {
+           return true;
+         }
+         if (ch == string_match_comma) {
+           // Get ready to match the next item.
+           matchp = string;
+           continue;
+         }
+         return false;  // End of all items.
+ 
+       case string_match_star:
+         if (matchp != nullptr) {
+           // Wildcard:  Parse out following anchor word and look for it.
+           const char* begp = patp;
+           const char* endp = patp;
+           int anchor_len = 0;
+           for (;;) {
+             // get as many following regular characters as possible
+             endp = patp;
+             ch = _pattern_getc(patp, pattern_end);
+             if (ch <= 0) {
+               break;
+             }
+             anchor_len += 1;
+           }
+           // Anchor word [begp..endp) does not contain ch, so back up.
+           // Now do an eager match to the anchor word, and commit to it.
+           patp = endp;
+           if (ch == string_match_eos ||
+               ch == string_match_comma) {
+             // Anchor word is at end of pattern, so treat it as a fixed pattern.
+             const char* limitp = string_end - anchor_len;
+             matchp = limitp;
+             patp = begp;
+             // Resume normal scanning at the only possible match position.
+             continue;
+           }
+           // Find a floating occurrence of the anchor and continue matching.
+           // Note:  This is greedy; there is no backtrack here.  Good enough.
+           matchp = skip_anchor_word(matchp, string_end, anchor_len, begp, endp);
+         }
+         continue;
+       }
+       // Normal character.
+       if (matchp != nullptr) {
+         int mch = _string_getc(matchp, string_end);
+         if (mch != ch) {
+           matchp = nullptr;
+         }
+       }
+     }
+   }
+ };
+ 
+ // Match a wildcarded class list to a proposed class name (in internal form).
+ // Commas or newlines separate multiple possible matches; stars are shell-style wildcards.
+ class ClassListMatcher : public StringMatcher {
+  public:
+   ClassListMatcher()
+     : StringMatcher(pattern_list_getc, class_name_getc)
+   { }
+ 
+  private:
+   static int pattern_list_getc(const char* &pattern_ptr,
+                                const char* pattern_end) {
+     if (pattern_ptr == pattern_end) {
+       return string_match_eos;
+     }
+     int ch = (unsigned char) *pattern_ptr++;
+     switch (ch) {
+     case ' ': case '\t': case '\n': case '\r':
+     case ',':
+       // End of list item.
+       for (;;) {
+         switch (*pattern_ptr) {
+         case ' ': case '\t': case '\n': case '\r':
+         case ',':
+           pattern_ptr += 1;  // Collapse multiple commas or spaces.
+           continue;
+         }
+         break;
+       }
+       return string_match_comma;
+ 
+     case '*':
+       // Wildcard, matching any number of chars.
+       while (*pattern_ptr == '*') {
+         pattern_ptr += 1;  // Collapse multiple stars.
+       }
+       return string_match_star;
+ 
+     case '.':
+       ch = '/';   // Look for internal form of package separator
+       break;
+ 
+     case '\\':
+       // Superquote in pattern escapes * , whitespace, and itself.
+       if (pattern_ptr < pattern_end) {
+         ch = (unsigned char) *pattern_ptr++;
+       }
+       break;
+     }
+ 
+     assert(ch > 0, "regular char only");
+     return ch;
+   }
+ 
+   static int class_name_getc(const char* &name_ptr,
+                              const char* name_end) {
+     if (name_ptr == name_end) {
+       return string_match_eos;
+     }
+     int ch = (unsigned char) *name_ptr++;
+     if (ch == '.') {
+       ch = '/';   // Normalize to internal form of package separator
+     }
+     return ch;  // plain character
+   }
+ };
+ 
+ bool StringUtils::class_list_match(const char* class_pattern_list,
+                                    const char* class_name) {
+   if (class_pattern_list == nullptr || class_name == nullptr || class_name[0] == '\0')
+     return false;
+   ClassListMatcher clm;
+   return clm.string_match(class_pattern_list, class_name);
+ }
+ 
+ 
  const char* StringUtils::strstr_nocase(const char* haystack, const char* needle) {
    if (needle[0] == '\0') {
      return haystack; // empty needle matches with anything
    }
    for (size_t i = 0; haystack[i] != '\0'; i++) {
< prev index next >