Reduce stack consumption for Demangle(). Fixes issue 81.
author <hlsyounes@gmail.com> <>
Fri, 25 Feb 2011 14:33:28 +0000 (14:33 +0000)
committer <hlsyounes@gmail.com> <>
Fri, 25 Feb 2011 14:33:28 +0000 (14:33 +0000)
git-svn-id: https://google-glog.googlecode.com/svn/trunk@92 eb4d4688-79bd-11dd-afb4-1d65580434c0

src/demangle.cc

index cc7cebb..2fbb790 100644 (file)
@@ -143,14 +143,12 @@ static const AbbrevPair kSubstitutionList[] = {
 // State needed for demangling.
 typedef struct {
   const char *mangled_cur;  // Cursor of mangled name.
-  const char *mangled_end;  // End of mangled name.
   char *out_cur;            // Cursor of output string.
   const char *out_begin;    // Beginning of output string.
   const char *out_end;      // End of output string.
   const char *prev_name;    // For constructors/destructors.
   int prev_name_length;     // For constructors/destructors.
-  int nest_level;           // For nested names.
-  int number;               // Remember the previous number.
+  short nest_level;         // For nested names.
   bool append;              // Append flag.
   bool overflowed;          // True if output gets overflowed.
 } State;
@@ -166,6 +164,16 @@ static size_t StrLen(const char *str) {
   return len;
 }
 
+// Returns true if "str" has at least "n" characters remaining.
+static bool AtLeastNumCharsRemaining(const char *str, int n) {
+  for (int i = 0; i < n; ++i) {
+    if (str == '\0') {
+      return false;
+    }
+  }
+  return true;
+}
+
 // Returns true if "str" has "prefix" as a prefix.
 static bool StrPrefix(const char *str, const char *prefix) {
   size_t i = 0;
@@ -179,39 +187,33 @@ static bool StrPrefix(const char *str, const char *prefix) {
 static void InitState(State *state, const char *mangled,
                       char *out, int out_size) {
   state->mangled_cur = mangled;
-  state->mangled_end = mangled + StrLen(mangled);
   state->out_cur = out;
   state->out_begin = out;
   state->out_end = out + out_size;
   state->prev_name  = NULL;
   state->prev_name_length = -1;
   state->nest_level = -1;
-  state->number = -1;
   state->append = true;
   state->overflowed = false;
 }
 
-// Calculates the remaining length of the mangled name.
-static int RemainingLength(State *state) {
-  return state->mangled_end - state->mangled_cur;
-}
-
-// Returns true and advances "mangled_cur" if we find "c" at
-// "mangled_cur" position.
-static bool ParseChar(State *state, const char c) {
-  if (RemainingLength(state) >= 1 && *state->mangled_cur == c) {
+// Returns true and advances "mangled_cur" if we find "one_char_token"
+// at "mangled_cur" position.  It is assumed that "one_char_token" does
+// not contain '\0'.
+static bool ParseOneCharToken(State *state, const char one_char_token) {
+  if (state->mangled_cur[0] == one_char_token) {
     ++state->mangled_cur;
     return true;
   }
   return false;
 }
 
-// Returns true and advances "mangled_cur" if we find "two_chars" at
-// "mangled_cur" position.
-static bool ParseTwoChar(State *state, const char *two_chars) {
-  if (RemainingLength(state) >= 2 &&
-      state->mangled_cur[0] == two_chars[0] &&
-      state->mangled_cur[1] == two_chars[1]) {
+// Returns true and advances "mangled_cur" if we find "two_char_token"
+// at "mangled_cur" position.  It is assumed that "two_char_token" does
+// not contain '\0'.
+static bool ParseTwoCharToken(State *state, const char *two_char_token) {
+  if (state->mangled_cur[0] == two_char_token[0] &&
+      state->mangled_cur[1] == two_char_token[1]) {
     state->mangled_cur += 2;
     return true;
   }
@@ -221,13 +223,13 @@ static bool ParseTwoChar(State *state, const char *two_chars) {
 // Returns true and advances "mangled_cur" if we find any character in
 // "char_class" at "mangled_cur" position.
 static bool ParseCharClass(State *state, const char *char_class) {
-  if (state->mangled_cur == state->mangled_end) {
+  if (state->mangled_cur == '\0') {
     return false;
   }
   const char *p = char_class;
   for (; *p != '\0'; ++p) {
-    if (*state->mangled_cur == *p) {
-      state->mangled_cur += 1;
+    if (state->mangled_cur[0] == *p) {
+      ++state->mangled_cur;
       return true;
     }
   }
@@ -251,8 +253,9 @@ static bool OneOrMore(ParseFunc parse_func, State *state) {
 }
 
 // This function is used for handling <non-terminal>* syntax. The function
-// always returns true and must be followed by a termination symbol or a
-// terminating sequence not handled by parse_func (e.g. ParseChar(state, 'E')).
+// always returns true and must be followed by a termination token or a
+// terminating sequence not handled by parse_func (e.g.
+// ParseOneCharToken(state, 'E')).
 static bool ZeroOrMore(ParseFunc parse_func, State *state) {
   while (parse_func(state)) {
   }
@@ -284,7 +287,7 @@ static bool IsLower(char c) {
 }
 
 static bool IsAlpha(char c) {
-  return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
 }
 
 static bool IsDigit(char c) {
@@ -353,7 +356,7 @@ static bool EnterNestedName(State *state) {
 }
 
 // This function is used for handling nested names.
-static bool LeaveNestedName(State *state, int prev_value) {
+static bool LeaveNestedName(State *state, short prev_value) {
   state->nest_level = prev_value;
   return true;
 }
@@ -393,11 +396,11 @@ static void MaybeCancelLastSeparator(State *state) {
   }
 }
 
-// Returns true if identifier pointed by "mangled_cur" is anonymous
-// namespace.
-static bool IdentifierIsAnonymousNamespace(State *state) {
-  const char anon_prefix[] = "_GLOBAL__N_";
-  return (state->number > sizeof(anon_prefix) - 1 &&  // Should be longer.
+// Returns true if the identifier of the given length pointed to by
+// "mangled_cur" is anonymous namespace.
+static bool IdentifierIsAnonymousNamespace(State *state, int length) {
+  static const char anon_prefix[] = "_GLOBAL__N_";
+  return (length > sizeof(anon_prefix) - 1 &&  // Should be longer.
           StrPrefix(state->mangled_cur, anon_prefix));
 }
 
@@ -412,10 +415,10 @@ static bool ParsePrefix(State *state);
 static bool ParseUnqualifiedName(State *state);
 static bool ParseSourceName(State *state);
 static bool ParseLocalSourceName(State *state);
-static bool ParseNumber(State *state);
+static bool ParseNumber(State *state, int *number_out);
 static bool ParseFloatNumber(State *state);
 static bool ParseSeqId(State *state);
-static bool ParseIdentifier(State *state);
+static bool ParseIdentifier(State *state, int length);
 static bool ParseOperatorName(State *state);
 static bool ParseSpecialName(State *state);
 static bool ParseCallOffset(State *state);
@@ -472,21 +475,7 @@ static bool ParseSubstitution(State *state);
 
 // <mangled-name> ::= _Z <encoding>
 static bool ParseMangledName(State *state) {
-  if (ParseTwoChar(state, "_Z") && ParseEncoding(state)) {
-    // Drop trailing function clone suffix, if any.
-    if (IsFunctionCloneSuffix(state->mangled_cur)) {
-      state->mangled_cur = state->mangled_end;
-    }
-    // Append trailing version suffix if any.
-    // ex. _Z3foo@@GLIBCXX_3.4
-    if (state->mangled_cur < state->mangled_end &&
-        state->mangled_cur[0] == '@') {
-      MaybeAppend(state, state->mangled_cur);
-      state->mangled_cur = state->mangled_end;
-    }
-    return true;
-  }
-  return false;
+  return ParseTwoCharToken(state, "_Z") && ParseEncoding(state);
 }
 
 // <encoding> ::= <(function) name> <bare-function-type>
@@ -536,7 +525,7 @@ static bool ParseUnscopedName(State *state) {
   }
 
   State copy = *state;
-  if (ParseTwoChar(state, "St") &&
+  if (ParseTwoCharToken(state, "St") &&
       MaybeAppend(state, "std::") &&
       ParseUnqualifiedName(state)) {
     return true;
@@ -555,12 +544,12 @@ static bool ParseUnscopedTemplateName(State *state) {
 //               ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
 static bool ParseNestedName(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'N') &&
+  if (ParseOneCharToken(state, 'N') &&
       EnterNestedName(state) &&
       Optional(ParseCVQualifiers(state)) &&
       ParsePrefix(state) &&
       LeaveNestedName(state, copy.nest_level) &&
-      ParseChar(state, 'E')) {
+      ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
@@ -613,7 +602,8 @@ static bool ParseUnqualifiedName(State *state) {
 // <source-name> ::= <positive length number> <identifier>
 static bool ParseSourceName(State *state) {
   State copy = *state;
-  if (ParseNumber(state) && ParseIdentifier(state)) {
+  int length = -1;
+  if (ParseNumber(state, &length) && ParseIdentifier(state, length)) {
     return true;
   }
   *state = copy;
@@ -627,7 +617,7 @@ static bool ParseSourceName(State *state) {
 //   http://gcc.gnu.org/viewcvs?view=rev&revision=124467
 static bool ParseLocalSourceName(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'L') && ParseSourceName(state) &&
+  if (ParseOneCharToken(state, 'L') && ParseSourceName(state) &&
       Optional(ParseDiscriminator(state))) {
     return true;
   }
@@ -636,14 +626,16 @@ static bool ParseLocalSourceName(State *state) {
 }
 
 // <number> ::= [n] <non-negative decimal integer>
-static bool ParseNumber(State *state) {
+// If "number_out" is non-null, then *number_out is set to the value of the
+// parsed number on success.
+static bool ParseNumber(State *state, int *number_out) {
   int sign = 1;
-  if (ParseChar(state, 'n')) {
+  if (ParseOneCharToken(state, 'n')) {
     sign = -1;
   }
   const char *p = state->mangled_cur;
   int number = 0;
-  for (;p < state->mangled_end; ++p) {
+  for (;*p != '\0'; ++p) {
     if (IsDigit(*p)) {
       number = number * 10 + (*p - '0');
     } else {
@@ -652,7 +644,9 @@ static bool ParseNumber(State *state) {
   }
   if (p != state->mangled_cur) {  // Conversion succeeded.
     state->mangled_cur = p;
-    state->number = number * sign;
+    if (number_out != NULL) {
+      *number_out = number * sign;
+    }
     return true;
   }
   return false;
@@ -662,19 +656,13 @@ static bool ParseNumber(State *state) {
 // hexadecimal string.
 static bool ParseFloatNumber(State *state) {
   const char *p = state->mangled_cur;
-  int number = 0;
-  for (;p < state->mangled_end; ++p) {
-    if (IsDigit(*p)) {
-      number = number * 16 + (*p - '0');
-    } else if (*p >= 'a' && *p <= 'f') {
-      number = number * 16 + (*p - 'a' + 10);
-    } else {
+  for (;*p != '\0'; ++p) {
+    if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) {
       break;
     }
   }
   if (p != state->mangled_cur) {  // Conversion succeeded.
     state->mangled_cur = p;
-    state->number = number;
     return true;
   }
   return false;
@@ -684,37 +672,30 @@ static bool ParseFloatNumber(State *state) {
 // using digits and upper case letters
 static bool ParseSeqId(State *state) {
   const char *p = state->mangled_cur;
-  int number = 0;
-  for (;p < state->mangled_end; ++p) {
-    if (IsDigit(*p)) {
-      number = number * 36 + (*p - '0');
-    } else if (*p >= 'A' && *p <= 'Z') {
-      number = number * 36 + (*p - 'A' + 10);
-    } else {
+  for (;*p != '\0'; ++p) {
+    if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) {
       break;
     }
   }
   if (p != state->mangled_cur) {  // Conversion succeeded.
     state->mangled_cur = p;
-    state->number = number;
     return true;
   }
   return false;
 }
 
-// <identifier> ::= <unqualified source code identifier>
-static bool ParseIdentifier(State *state) {
-  if (state->number == -1 ||
-      RemainingLength(state) < state->number) {
+// <identifier> ::= <unqualified source code identifier> (of given length)
+static bool ParseIdentifier(State *state, int length) {
+  if (length == -1 ||
+      !AtLeastNumCharsRemaining(state->mangled_cur, length)) {
     return false;
   }
-  if (IdentifierIsAnonymousNamespace(state)) {
+  if (IdentifierIsAnonymousNamespace(state, length)) {
     MaybeAppend(state, "(anonymous namespace)");
   } else {
-    MaybeAppendWithLength(state, state->mangled_cur, state->number);
+    MaybeAppendWithLength(state, state->mangled_cur, length);
   }
-  state->mangled_cur += state->number;
-  state->number = -1;  // Reset the number.
+  state->mangled_cur += length;
   return true;
 }
 
@@ -722,12 +703,12 @@ static bool ParseIdentifier(State *state) {
 //                 ::= cv <type>  # (cast)
 //                 ::= v  <digit> <source-name> # vendor extended operator
 static bool ParseOperatorName(State *state) {
-  if (RemainingLength(state) < 2) {
+  if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) {
     return false;
   }
   // First check with "cv" (cast) case.
   State copy = *state;
-  if (ParseTwoChar(state, "cv") &&
+  if (ParseTwoCharToken(state, "cv") &&
       MaybeAppend(state, "operator ") &&
       EnterNestedName(state) &&
       ParseType(state) &&
@@ -737,7 +718,7 @@ static bool ParseOperatorName(State *state) {
   *state = copy;
 
   // Then vendor extended operators.
-  if (ParseChar(state, 'v') && ParseCharClass(state, "0123456789") &&
+  if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") &&
       ParseSourceName(state)) {
     return true;
   }
@@ -786,34 +767,34 @@ static bool ParseOperatorName(State *state) {
 // stack traces.  The are special data.
 static bool ParseSpecialName(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'T') &&
+  if (ParseOneCharToken(state, 'T') &&
       ParseCharClass(state, "VTIS") &&
       ParseType(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "Tc") && ParseCallOffset(state) &&
+  if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) &&
       ParseCallOffset(state) && ParseEncoding(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "GV") &&
+  if (ParseTwoCharToken(state, "GV") &&
       ParseName(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'T') && ParseCallOffset(state) &&
+  if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) &&
       ParseEncoding(state)) {
     return true;
   }
   *state = copy;
 
   // G++ extensions
-  if (ParseTwoChar(state, "TC") && ParseType(state) &&
-      ParseNumber(state) && ParseChar(state, '_') &&
+  if (ParseTwoCharToken(state, "TC") && ParseType(state) &&
+      ParseNumber(state, NULL) && ParseOneCharToken(state, '_') &&
       DisableAppend(state) &&
       ParseType(state)) {
     RestoreAppend(state, copy.append);
@@ -821,23 +802,23 @@ static bool ParseSpecialName(State *state) {
   }
   *state = copy;
 
-  if (ParseChar(state, 'T') && ParseCharClass(state, "FJ") &&
+  if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") &&
       ParseType(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "GR") && ParseName(state)) {
+  if (ParseTwoCharToken(state, "GR") && ParseName(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "GA") && ParseEncoding(state)) {
+  if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'T') && ParseCharClass(state, "hv") &&
+  if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") &&
       ParseCallOffset(state) && ParseEncoding(state)) {
     return true;
   }
@@ -849,14 +830,14 @@ static bool ParseSpecialName(State *state) {
 //               ::= v <v-offset> _
 static bool ParseCallOffset(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'h') &&
-      ParseNVOffset(state) && ParseChar(state, '_')) {
+  if (ParseOneCharToken(state, 'h') &&
+      ParseNVOffset(state) && ParseOneCharToken(state, '_')) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'v') &&
-      ParseVOffset(state) && ParseChar(state, '_')) {
+  if (ParseOneCharToken(state, 'v') &&
+      ParseVOffset(state) && ParseOneCharToken(state, '_')) {
     return true;
   }
   *state = copy;
@@ -866,14 +847,14 @@ static bool ParseCallOffset(State *state) {
 
 // <nv-offset> ::= <(offset) number>
 static bool ParseNVOffset(State *state) {
-  return ParseNumber(state);
+  return ParseNumber(state, NULL);
 }
 
 // <v-offset>  ::= <(offset) number> _ <(virtual offset) number>
 static bool ParseVOffset(State *state) {
   State copy = *state;
-  if (ParseNumber(state) && ParseChar(state, '_') &&
-      ParseNumber(state)) {
+  if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') &&
+      ParseNumber(state, NULL)) {
     return true;
   }
   *state = copy;
@@ -884,7 +865,7 @@ static bool ParseVOffset(State *state) {
 //                  ::= D0 | D1 | D2
 static bool ParseCtorDtorName(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'C') &&
+  if (ParseOneCharToken(state, 'C') &&
       ParseCharClass(state, "123")) {
     const char * const prev_name = state->prev_name;
     const int prev_name_length = state->prev_name_length;
@@ -893,7 +874,7 @@ static bool ParseCtorDtorName(State *state) {
   }
   *state = copy;
 
-  if (ParseChar(state, 'D') &&
+  if (ParseOneCharToken(state, 'D') &&
       ParseCharClass(state, "012")) {
     const char * const prev_name = state->prev_name;
     const int prev_name_length = state->prev_name_length;
@@ -938,18 +919,18 @@ static bool ParseType(State *state) {
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "Dp") && ParseType(state)) {
+  if (ParseTwoCharToken(state, "Dp") && ParseType(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'D') && ParseCharClass(state, "tT") &&
-      ParseExpression(state) && ParseChar(state, 'E')) {
+  if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") &&
+      ParseExpression(state) && ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'U') && ParseSourceName(state) &&
+  if (ParseOneCharToken(state, 'U') && ParseSourceName(state) &&
       ParseType(state)) {
     return true;
   }
@@ -983,9 +964,9 @@ static bool ParseType(State *state) {
 // ParseType().
 static bool ParseCVQualifiers(State *state) {
   int num_cv_qualifiers = 0;
-  num_cv_qualifiers += ParseChar(state, 'r');
-  num_cv_qualifiers += ParseChar(state, 'V');
-  num_cv_qualifiers += ParseChar(state, 'K');
+  num_cv_qualifiers += ParseOneCharToken(state, 'r');
+  num_cv_qualifiers += ParseOneCharToken(state, 'V');
+  num_cv_qualifiers += ParseOneCharToken(state, 'K');
   return num_cv_qualifiers > 0;
 }
 
@@ -1002,7 +983,7 @@ static bool ParseBuiltinType(State *state) {
   }
 
   State copy = *state;
-  if (ParseChar(state, 'u') && ParseSourceName(state)) {
+  if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) {
     return true;
   }
   *state = copy;
@@ -1012,8 +993,9 @@ static bool ParseBuiltinType(State *state) {
 // <function-type> ::= F [Y] <bare-function-type> E
 static bool ParseFunctionType(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'F') && Optional(ParseChar(state, 'Y')) &&
-      ParseBareFunctionType(state) && ParseChar(state, 'E')) {
+  if (ParseOneCharToken(state, 'F') &&
+      Optional(ParseOneCharToken(state, 'Y')) &&
+      ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
@@ -1042,14 +1024,14 @@ static bool ParseClassEnumType(State *state) {
 //              ::= A [<(dimension) expression>] _ <(element) type>
 static bool ParseArrayType(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'A') && ParseNumber(state) &&
-      ParseChar(state, '_') && ParseType(state)) {
+  if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) &&
+      ParseOneCharToken(state, '_') && ParseType(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'A') && Optional(ParseExpression(state)) &&
-      ParseChar(state, '_') && ParseType(state)) {
+  if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) &&
+      ParseOneCharToken(state, '_') && ParseType(state)) {
     return true;
   }
   *state = copy;
@@ -1059,7 +1041,7 @@ static bool ParseArrayType(State *state) {
 // <pointer-to-member-type> ::= M <(class) type> <(member) type>
 static bool ParsePointerToMemberType(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'M') && ParseType(state) &&
+  if (ParseOneCharToken(state, 'M') && ParseType(state) &&
       ParseType(state)) {
     return true;
   }
@@ -1070,14 +1052,14 @@ static bool ParsePointerToMemberType(State *state) {
 // <template-param> ::= T_
 //                  ::= T <parameter-2 non-negative number> _
 static bool ParseTemplateParam(State *state) {
-  if (ParseTwoChar(state, "T_")) {
+  if (ParseTwoCharToken(state, "T_")) {
     MaybeAppend(state, "?");  // We don't support template substitutions.
     return true;
   }
 
   State copy = *state;
-  if (ParseChar(state, 'T') && ParseNumber(state) &&
-      ParseChar(state, '_')) {
+  if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) &&
+      ParseOneCharToken(state, '_')) {
     MaybeAppend(state, "?");  // We don't support template substitutions.
     return true;
   }
@@ -1097,9 +1079,9 @@ static bool ParseTemplateTemplateParam(State *state) {
 static bool ParseTemplateArgs(State *state) {
   State copy = *state;
   DisableAppend(state);
-  if (ParseChar(state, 'I') &&
+  if (ParseOneCharToken(state, 'I') &&
       OneOrMore(ParseTemplateArg, state) &&
-      ParseChar(state, 'E')) {
+      ParseOneCharToken(state, 'E')) {
     RestoreAppend(state, copy.append);
     MaybeAppend(state, "<>");
     return true;
@@ -1114,9 +1096,9 @@ static bool ParseTemplateArgs(State *state) {
 //                 ::= X <expression> E
 static bool ParseTemplateArg(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'I') &&
+  if (ParseOneCharToken(state, 'I') &&
       ZeroOrMore(ParseTemplateArg, state) &&
-      ParseChar(state, 'E')) {
+      ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
@@ -1127,8 +1109,8 @@ static bool ParseTemplateArg(State *state) {
   }
   *state = copy;
 
-  if (ParseChar(state, 'X') && ParseExpression(state) &&
-      ParseChar(state, 'E')) {
+  if (ParseOneCharToken(state, 'X') && ParseExpression(state) &&
+      ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
@@ -1171,19 +1153,19 @@ static bool ParseExpression(State *state) {
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "st") && ParseType(state)) {
+  if (ParseTwoCharToken(state, "st") && ParseType(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "sr") && ParseType(state) &&
+  if (ParseTwoCharToken(state, "sr") && ParseType(state) &&
       ParseUnqualifiedName(state) &&
       ParseTemplateArgs(state)) {
     return true;
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "sr") && ParseType(state) &&
+  if (ParseTwoCharToken(state, "sr") && ParseType(state) &&
       ParseUnqualifiedName(state)) {
     return true;
   }
@@ -1198,28 +1180,28 @@ static bool ParseExpression(State *state) {
 //                ::= LZ <encoding> E
 static bool ParseExprPrimary(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'L') && ParseType(state) &&
-      ParseNumber(state) &&
-      ParseChar(state, 'E')) {
+  if (ParseOneCharToken(state, 'L') && ParseType(state) &&
+      ParseNumber(state, NULL) &&
+      ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'L') && ParseType(state) &&
+  if (ParseOneCharToken(state, 'L') && ParseType(state) &&
       ParseFloatNumber(state) &&
-      ParseChar(state, 'E')) {
+      ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'L') && ParseMangledName(state) &&
-      ParseChar(state, 'E')) {
+  if (ParseOneCharToken(state, 'L') && ParseMangledName(state) &&
+      ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
 
-  if (ParseTwoChar(state, "LZ") && ParseEncoding(state) &&
-      ParseChar(state, 'E')) {
+  if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) &&
+      ParseOneCharToken(state, 'E')) {
     return true;
   }
   *state = copy;
@@ -1232,15 +1214,15 @@ static bool ParseExprPrimary(State *state) {
 //              := Z <(function) encoding> E s [<discriminator>]
 static bool ParseLocalName(State *state) {
   State copy = *state;
-  if (ParseChar(state, 'Z') && ParseEncoding(state) &&
-      ParseChar(state, 'E') && MaybeAppend(state, "::") &&
+  if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) &&
+      ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") &&
       ParseName(state) && Optional(ParseDiscriminator(state))) {
     return true;
   }
   *state = copy;
 
-  if (ParseChar(state, 'Z') && ParseEncoding(state) &&
-      ParseTwoChar(state, "Es") && Optional(ParseDiscriminator(state))) {
+  if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) &&
+      ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) {
     return true;
   }
   *state = copy;
@@ -1250,7 +1232,7 @@ static bool ParseLocalName(State *state) {
 // <discriminator> := _ <(non-negative) number>
 static bool ParseDiscriminator(State *state) {
   State copy = *state;
-  if (ParseChar(state, '_') && ParseNumber(state)) {
+  if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) {
     return true;
   }
   *state = copy;
@@ -1261,21 +1243,21 @@ static bool ParseDiscriminator(State *state) {
 //                ::= S <seq-id> _
 //                ::= St, etc.
 static bool ParseSubstitution(State *state) {
-  if (ParseTwoChar(state, "S_")) {
+  if (ParseTwoCharToken(state, "S_")) {
     MaybeAppend(state, "?");  // We don't support substitutions.
     return true;
   }
 
   State copy = *state;
-  if (ParseChar(state, 'S') && ParseSeqId(state) &&
-      ParseChar(state, '_')) {
+  if (ParseOneCharToken(state, 'S') && ParseSeqId(state) &&
+      ParseOneCharToken(state, '_')) {
     MaybeAppend(state, "?");  // We don't support substitutions.
     return true;
   }
   *state = copy;
 
   // Expand abbreviations like "St" => "std".
-  if (ParseChar(state, 'S')) {
+  if (ParseOneCharToken(state, 'S')) {
     const AbbrevPair *p;
     for (p = kSubstitutionList; p->abbrev != NULL; ++p) {
       if (state->mangled_cur[0] == p->abbrev[1]) {
@@ -1284,7 +1266,7 @@ static bool ParseSubstitution(State *state) {
           MaybeAppend(state, "::");
           MaybeAppend(state, p->real_name);
         }
-        state->mangled_cur += 1;
+        ++state->mangled_cur;
         return true;
       }
     }
@@ -1293,13 +1275,33 @@ static bool ParseSubstitution(State *state) {
   return false;
 }
 
+// Parse <mangled-name>, optionally followed by either a function-clone suffix
+// or version suffix.  Returns true only if all of "mangled_cur" was consumed.
+static bool ParseTopLevelMangledName(State *state) {
+  if (ParseMangledName(state)) {
+    if (state->mangled_cur[0] != '\0') {
+      // Drop trailing function clone suffix, if any.
+      if (IsFunctionCloneSuffix(state->mangled_cur)) {
+        return true;
+      }
+      // Append trailing version suffix if any.
+      // ex. _Z3foo@@GLIBCXX_3.4
+      if (state->mangled_cur[0] == '@') {
+        MaybeAppend(state, state->mangled_cur);
+        return true;
+      }
+      return false;  // Unconsumed suffix.
+    }
+    return true;
+  }
+  return false;
+}
+
 // The demangler entry point.
 bool Demangle(const char *mangled, char *out, int out_size) {
   State state;
   InitState(&state, mangled, out, out_size);
-  return (ParseMangledName(&state) &&
-          state.overflowed == false &&
-          RemainingLength(&state) == 0);
+  return ParseTopLevelMangledName(&state) && !state.overflowed;
 }
 
 _END_GOOGLE_NAMESPACE_