From b28683f8027bf1e886b748b5603eb16d203b5a92 Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 18:18:33 -0500
Subject: [PATCH 01/11] protoc-gen-c/c_helpers.h: Move compat defines into new
header file compat.h

---
protoc-gen-c/c_field.cc           |  1 +
protoc-gen-c/c_helpers.cc         |  1 +
protoc-gen-c/c_helpers.h          | 10 -------
protoc-gen-c/c_message.cc         |  1 +
protoc-gen-c/c_primitive_field.cc |  1 +
protoc-gen-c/compat.h             | 46 +++++++++++++++++++++++++++++++
protoc-gen-c/main.cc              |  1 +
7 files changed, 51 insertions(+), 10 deletions(-)
create mode 100644 protoc-gen-c/compat.h

diff --git a/protoc-gen-c/c_field.cc b/protoc-gen-c/c_field.cc
index 5e79967b..d6d8597e 100644
--- a/protoc-gen-c/c_field.cc
+++ b/protoc-gen-c/c_field.cc
@@ -74,6 +74,7 @@
#include "c_message_field.h"
#include "c_primitive_field.h"
#include "c_string_field.h"
+#include "compat.h"

namespace protobuf_c {

diff --git a/protoc-gen-c/c_helpers.cc b/protoc-gen-c/c_helpers.cc
index 5edcf904..c38843f8 100644
--- a/protoc-gen-c/c_helpers.cc
+++ b/protoc-gen-c/c_helpers.cc
@@ -73,6 +73,7 @@
#include <google/protobuf/stubs/common.h>

#include "c_helpers.h"
+#include "compat.h"

namespace protobuf_c {

diff --git a/protoc-gen-c/c_helpers.h b/protoc-gen-c/c_helpers.h
index 943676e9..e69504bb 100644
--- a/protoc-gen-c/c_helpers.h
+++ b/protoc-gen-c/c_helpers.h
@@ -186,16 +186,6 @@ inline int FieldSyntax(const google::protobuf::FieldDescriptor* field) {
  return 2;
}

-// Work around changes in protobuf >= 22.x without breaking compilation against
-// older protobuf versions.
-#if GOOGLE_PROTOBUF_VERSION >= 4022000
-# define GOOGLE_ARRAYSIZE      ABSL_ARRAYSIZE
-# define GOOGLE_CHECK_EQ       ABSL_CHECK_EQ
-# define GOOGLE_CHECK_EQ       ABSL_CHECK_EQ
-# define GOOGLE_DCHECK_GE      ABSL_DCHECK_GE
-# define GOOGLE_LOG            ABSL_LOG
-#endif
-
}  // namespace protobuf_c

#endif  // PROTOBUF_C_PROTOC_GEN_C_C_HELPERS_H__
diff --git a/protoc-gen-c/c_message.cc b/protoc-gen-c/c_message.cc
index d4a9a01e..46413873 100644
--- a/protoc-gen-c/c_message.cc
+++ b/protoc-gen-c/c_message.cc
@@ -78,6 +78,7 @@
#include "c_extension.h"
#include "c_helpers.h"
#include "c_message.h"
+#include "compat.h"

namespace protobuf_c {

diff --git a/protoc-gen-c/c_primitive_field.cc b/protoc-gen-c/c_primitive_field.cc
index 253b00bd..588f60e6 100644
--- a/protoc-gen-c/c_primitive_field.cc
+++ b/protoc-gen-c/c_primitive_field.cc
@@ -67,6 +67,7 @@

#include "c_helpers.h"
#include "c_primitive_field.h"
+#include "compat.h"

namespace protobuf_c {

diff --git a/protoc-gen-c/compat.h b/protoc-gen-c/compat.h
new file mode 100644
index 00000000..2ee78281
--- /dev/null
+++ b/protoc-gen-c/compat.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2008-2025, Dave Benson and the protobuf-c authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef PROTOBUF_C_PROTOC_GEN_C_COMPAT_H__
+#define PROTOBUF_C_PROTOC_GEN_C_COMPAT_H__
+
+#if GOOGLE_PROTOBUF_VERSION >= 4022000
+# define GOOGLE_ARRAYSIZE      ABSL_ARRAYSIZE
+# define GOOGLE_CHECK_EQ       ABSL_CHECK_EQ
+# define GOOGLE_DCHECK_GE      ABSL_DCHECK_GE
+# define GOOGLE_LOG            ABSL_LOG
+#endif
+
+namespace protobuf_c {
+
+namespace compat {
+
+}  // namespace compat
+
+}  // namespace protobuf_c
+
+#endif  // PROTOBUF_C_PROTOC_GEN_C_COMPAT_H__
diff --git a/protoc-gen-c/main.cc b/protoc-gen-c/main.cc
index 0656c113..5ab929d3 100644
--- a/protoc-gen-c/main.cc
+++ b/protoc-gen-c/main.cc
@@ -32,6 +32,7 @@

#include "c_generator.h"
#include "c_helpers.h"
+#include "compat.h"

int main(int argc, char* argv[]) {
  protobuf_c::CGenerator c_generator;

From 1678f1fba6f2d3e5c1db2817495ddcd72bd4e87b Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 20:09:03 -0500
Subject: [PATCH 02/11] protoc-gen-c/compat.h: Add `compat::StringView` type

protobuf >= 30.x replaces `const std::string&` in various APIs with
its own string view type that may actually be a `absl::string_view`.
Introduce our own `compat::StringView` type that we can use instead
of `const std::string&` in order to support compiling across multiple
protobuf versions.
---
protoc-gen-c/compat.h | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/protoc-gen-c/compat.h b/protoc-gen-c/compat.h
index 2ee78281..fe8041b5 100644
--- a/protoc-gen-c/compat.h
+++ b/protoc-gen-c/compat.h
@@ -28,6 +28,8 @@
#ifndef PROTOBUF_C_PROTOC_GEN_C_COMPAT_H__
#define PROTOBUF_C_PROTOC_GEN_C_COMPAT_H__

+#include <string>
+
#if GOOGLE_PROTOBUF_VERSION >= 4022000
# define GOOGLE_ARRAYSIZE      ABSL_ARRAYSIZE
# define GOOGLE_CHECK_EQ       ABSL_CHECK_EQ
@@ -39,6 +41,12 @@ namespace protobuf_c {

namespace compat {

+#if GOOGLE_PROTOBUF_VERSION >= 6030000
+typedef google::protobuf::internal::DescriptorStringView StringView;
+#else
+typedef const std::string& StringView;
+#endif
+
}  // namespace compat

}  // namespace protobuf_c

From db5252c131c82fb48ee599179b6989a577b7fbc8 Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 20:13:44 -0500
Subject: [PATCH 03/11] Remove some unused functions

---
protoc-gen-c/c_helpers.cc | 64 ---------------------------------------
protoc-gen-c/c_helpers.h  |  3 --
2 files changed, 67 deletions(-)

diff --git a/protoc-gen-c/c_helpers.cc b/protoc-gen-c/c_helpers.cc
index c38843f8..bbb4a615 100644
--- a/protoc-gen-c/c_helpers.cc
+++ b/protoc-gen-c/c_helpers.cc
@@ -90,14 +90,6 @@ namespace protobuf_c {
#pragma warning(disable:4996)
#endif

-std::string DotsToUnderscores(const std::string& name) {
-  return StringReplace(name, ".", "_", true);
-}
-
-std::string DotsToColons(const std::string& name) {
-  return StringReplace(name, ".", "::", true);
-}
-
std::string SimpleFtoa(float f) {
  char buf[100];
  snprintf(buf,sizeof(buf),"%.*g", FLT_DIG, f);
@@ -333,11 +325,6 @@ std::string FilenameIdentifier(const std::string& filename) {
  return result;
}

-// Return the name of the BuildDescriptors() function for a given file.
-std::string GlobalBuildDescriptorsName(const std::string& filename) {
-  return "proto_BuildDescriptors_" + FilenameIdentifier(filename);
-}
-
std::string GetLabelName(google::protobuf::FieldDescriptor::Label label) {
  switch (label) {
    case google::protobuf::FieldDescriptor::LABEL_OPTIONAL: return "optional";
@@ -392,57 +379,6 @@ WriteIntRanges(google::protobuf::io::Printer* printer, int n_values, const int *
  }
}

-
-
-// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
-// XXXXXXXXX  this stuff is copied from strutils.cc !!!!   XXXXXXXXXXXXXXXXXXXXXXXXXXXXx
-// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
-// ----------------------------------------------------------------------
-// StringReplace()
-//    Replace the "old" pattern with the "new" pattern in a string,
-//    and append the result to "res".  If replace_all is false,
-//    it only replaces the first instance of "old."
-// ----------------------------------------------------------------------
-
-void StringReplace(const std::string& s, const std::string& oldsub,
-                   const std::string& newsub, bool replace_all,
-                   std::string* res) {
-  if (oldsub.empty()) {
-    res->append(s);  // if empty, append the given string.
-    return;
-  }
-
-  std::string::size_type start_pos = 0;
-  std::string::size_type pos;
-  do {
-    pos = s.find(oldsub, start_pos);
-    if (pos == std::string::npos) {
-      break;
-    }
-    res->append(s, start_pos, pos - start_pos);
-    res->append(newsub);
-    start_pos = pos + oldsub.size();  // start searching again after the "old"
-  } while (replace_all);
-  res->append(s, start_pos, s.length() - start_pos);
-}
-
-
-// ----------------------------------------------------------------------
-// StringReplace()
-//    Give me a string and two patterns "old" and "new", and I replace
-//    the first instance of "old" in the string with "new", if it
-//    exists.  If "global" is true; call this repeatedly until it
-//    fails.  RETURN a new string, regardless of whether the replacement
-//    happened or not.
-// ----------------------------------------------------------------------
-
-std::string StringReplace(const std::string& s, const std::string& oldsub,
-                          const std::string& newsub, bool replace_all) {
-  std::string ret;
-  StringReplace(s, oldsub, newsub, replace_all, &ret);
-  return ret;
-}
-
// ----------------------------------------------------------------------
// SplitStringUsing()
//    Split a string using a character delimiter. Append the components
diff --git a/protoc-gen-c/c_helpers.h b/protoc-gen-c/c_helpers.h
index e69504bb..377d4272 100644
--- a/protoc-gen-c/c_helpers.h
+++ b/protoc-gen-c/c_helpers.h
@@ -150,9 +150,6 @@ const char* DeclaredTypeMethodName(google::protobuf::FieldDescriptor::Type type)
// Convert a file name into a valid identifier.
std::string FilenameIdentifier(const std::string& filename);

-// Return the name of the BuildDescriptors() function for a given file.
-std::string GlobalBuildDescriptorsName(const std::string& filename);
-
// return 'required', 'optional', or 'repeated'
std::string GetLabelName(google::protobuf::FieldDescriptor::Label label);


From bc2cb66d908f016dd3f7082c8a6ad7c58bc03412 Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 20:18:05 -0500
Subject: [PATCH 04/11] Use `compat::StringView` type across various function
signatures

---
protoc-gen-c/c_helpers.cc | 44 +++++++++++++++++++++------------------
protoc-gen-c/c_helpers.h  | 36 ++++++++++++++++----------------
2 files changed, 42 insertions(+), 38 deletions(-)

diff --git a/protoc-gen-c/c_helpers.cc b/protoc-gen-c/c_helpers.cc
index bbb4a615..c759c8c2 100644
--- a/protoc-gen-c/c_helpers.cc
+++ b/protoc-gen-c/c_helpers.cc
@@ -96,6 +96,7 @@ std::string SimpleFtoa(float f) {
  buf[sizeof(buf)-1] = 0;              /* should NOT be necessary */
  return buf;
}
+
std::string SimpleDtoa(double d) {
  char buf[100];
  snprintf(buf,sizeof(buf),"%.*g", DBL_DIG, d);
@@ -103,7 +104,7 @@ std::string SimpleDtoa(double d) {
  return buf;
}

-std::string CamelToUpper(const std::string &name) {
+std::string CamelToUpper(compat::StringView name) {
  bool was_upper = true;               // suppress initial _
  std::string rv = "";
  int len = name.length();
@@ -120,7 +121,8 @@ std::string CamelToUpper(const std::string &name) {
  }
  return rv;
}
-std::string CamelToLower(const std::string &name) {
+
+std::string CamelToLower(compat::StringView name) {
  bool was_upper = true;               // suppress initial _
  std::string rv = "";
  int len = name.length();
@@ -138,8 +140,7 @@ std::string CamelToLower(const std::string &name) {
  return rv;
}

-
-std::string ToUpper(const std::string &name) {
+std::string ToUpper(compat::StringView name) {
  std::string rv = "";
  int len = name.length();
  for (int i = 0; i < len; i++) {
@@ -147,7 +148,8 @@ std::string ToUpper(const std::string &name) {
  }
  return rv;
}
-std::string ToLower(const std::string &name) {
+
+std::string ToLower(compat::StringView name) {
  std::string rv = "";
  int len = name.length();
  for (int i = 0; i < len; i++) {
@@ -155,7 +157,8 @@ std::string ToLower(const std::string &name) {
  }
  return rv;
}
-std::string ToCamel(const std::string &name) {
+
+std::string ToCamel(compat::StringView name) {
  std::string rv = "";
  int len = name.length();
  bool next_is_upper = true;
@@ -172,7 +175,7 @@ std::string ToCamel(const std::string &name) {
  return rv;
}

-std::string OverrideFullName(const std::string &full_name, const google::protobuf::FileDescriptor* file) {
+std::string OverrideFullName(compat::StringView full_name, const google::protobuf::FileDescriptor* file) {
  const ProtobufCFileOptions opt = file->options().GetExtension(pb_c_file);
  if (!opt.has_c_package())
    return full_name;
@@ -184,7 +187,7 @@ std::string OverrideFullName(const std::string &full_name, const google::protobu
  return new_name + full_name.substr(file->package().length());
}

-std::string FullNameToLower(const std::string &full_name, const google::protobuf::FileDescriptor* file) {
+std::string FullNameToLower(compat::StringView full_name, const google::protobuf::FileDescriptor* file) {
  std::vector<std::string> pieces;
  SplitStringUsing(OverrideFullName(full_name, file), ".", &pieces);
  std::string rv = "";
@@ -195,7 +198,8 @@ std::string FullNameToLower(const std::string &full_name, const google::protobuf
  }
  return rv;
}
-std::string FullNameToUpper(const std::string &full_name, const google::protobuf::FileDescriptor* file) {
+
+std::string FullNameToUpper(compat::StringView full_name, const google::protobuf::FileDescriptor* file) {
  std::vector<std::string> pieces;
  SplitStringUsing(OverrideFullName(full_name, file), ".", &pieces);
  std::string rv = "";
@@ -206,7 +210,8 @@ std::string FullNameToUpper(const std::string &full_name, const google::protobuf
  }
  return rv;
}
-std::string FullNameToC(const std::string &full_name, const google::protobuf::FileDescriptor* file) {
+
+std::string FullNameToC(compat::StringView full_name, const google::protobuf::FileDescriptor* file) {
  std::vector<std::string> pieces;
  SplitStringUsing(OverrideFullName(full_name, file), ".", &pieces);
  std::string rv = "";
@@ -248,7 +253,7 @@ void PrintComment(google::protobuf::io::Printer* printer, std::string comment)
   }
}

-std::string ConvertToSpaces(const std::string &input) {
+std::string ConvertToSpaces(compat::StringView input) {
  return std::string(input.size(), ' ');
}

@@ -259,8 +264,7 @@ int compare_name_indices_by_name(const void *a, const void *b)
  return strcmp (ni_a->name, ni_b->name);
}

-
-std::string CEscape(const std::string& src);
+std::string CEscape(compat::StringView src);

const char* const kKeywordList[] = {
  "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case",
@@ -300,7 +304,7 @@ std::string FieldDeprecated(const google::protobuf::FieldDescriptor* field) {
  return "";
}

-std::string StripProto(const std::string& filename) {
+std::string StripProto(compat::StringView filename) {
  if (HasSuffixString(filename, ".protodevel")) {
    return StripSuffixString(filename, ".protodevel");
  } else {
@@ -309,7 +313,7 @@ std::string StripProto(const std::string& filename) {
}

// Convert a file name into a valid identifier.
-std::string FilenameIdentifier(const std::string& filename) {
+std::string FilenameIdentifier(compat::StringView filename) {
  std::string result;
  for (unsigned i = 0; i < filename.size(); i++) {
    if (isalnum(filename[i])) {
@@ -335,7 +339,7 @@ std::string GetLabelName(google::protobuf::FieldDescriptor::Label label) {
}

unsigned
-WriteIntRanges(google::protobuf::io::Printer* printer, int n_values, const int *values, const std::string &name)
+WriteIntRanges(google::protobuf::io::Printer* printer, int n_values, const int *values, compat::StringView name)
{
  std::map<std::string, std::string> vars;
  vars["name"] = name;
@@ -389,7 +393,7 @@ WriteIntRanges(google::protobuf::io::Printer* printer, int n_values, const int *
// ----------------------------------------------------------------------
template <typename ITR>
static inline
-void SplitStringToIteratorUsing(const std::string& full,
+void SplitStringToIteratorUsing(compat::StringView full,
                                const char* delim,
                                ITR& result) {
  // Optimize the common case where delim is a single character.
@@ -422,7 +426,7 @@ void SplitStringToIteratorUsing(const std::string& full,
  }
}

-void SplitStringUsing(const std::string& full,
+void SplitStringUsing(compat::StringView full,
                      const char* delim,
                      std::vector<std::string>* result) {
  std::back_insert_iterator< std::vector<std::string> > it(*result);
@@ -435,7 +439,6 @@ char* FastHexToBuffer(int i, char* buffer)
  return buffer;
}

-
static int CEscapeInternal(const char* src, int src_len, char* dest,
                           int dest_len, bool use_hex) {
  const char* src_end = src + src_len;
@@ -478,7 +481,8 @@ static int CEscapeInternal(const char* src, int src_len, char* dest,
  dest[used] = '\0';   // doesn't count towards return value though
  return used;
}
-std::string CEscape(const std::string& src) {
+
+std::string CEscape(compat::StringView src) {
  const int dest_length = src.size() * 4 + 1; // Maximum possible expansion
  std::unique_ptr<char[]> dest(new char[dest_length]);
  const int len = CEscapeInternal(src.data(), src.size(),
diff --git a/protoc-gen-c/c_helpers.h b/protoc-gen-c/c_helpers.h
index 377d4272..ccd39ca2 100644
--- a/protoc-gen-c/c_helpers.h
+++ b/protoc-gen-c/c_helpers.h
@@ -73,6 +73,8 @@

#include <protobuf-c/protobuf-c.pb.h>

+#include "compat.h"
+
namespace protobuf_c {

// --- Borrowed from stubs. ---
@@ -84,11 +86,10 @@ template <typename T> std::string SimpleItoa(T n) {

std::string SimpleFtoa(float f);
std::string SimpleDtoa(double f);
-void SplitStringUsing(const std::string &str, const char *delim, std::vector<std::string> *out);
-std::string CEscape(const std::string& src);
-std::string StringReplace(const std::string& s, const std::string& oldsub, const std::string& newsub, bool replace_all);
-inline bool HasSuffixString(const std::string& str, const std::string& suffix) { return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; }
-inline std::string StripSuffixString(const std::string& str, const std::string& suffix) { if (HasSuffixString(str, suffix)) { return str.substr(0, str.size() - suffix.size()); } else { return str; } }
+void SplitStringUsing(compat::StringView str, const char *delim, std::vector<std::string> *out);
+std::string CEscape(compat::StringView src);
+inline bool HasSuffixString(compat::StringView str, compat::StringView suffix) { return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; }
+inline std::string StripSuffixString(compat::StringView str, compat::StringView suffix) { if (HasSuffixString(str, suffix)) { return str.substr(0, str.size() - suffix.size()); } else { return str; } }
char* FastHexToBuffer(int i, char* buffer);


@@ -110,31 +111,31 @@ inline const google::protobuf::Descriptor* FieldScope(const google::protobuf::Fi

// convert a CamelCase class name into an all uppercase affair
// with underscores separating words, e.g. MyClass becomes MY_CLASS.
-std::string CamelToUpper(const std::string &class_name);
-std::string CamelToLower(const std::string &class_name);
+std::string CamelToUpper(compat::StringView class_name);
+std::string CamelToLower(compat::StringView class_name);

// lowercased, underscored name to camel case
-std::string ToCamel(const std::string &name);
+std::string ToCamel(compat::StringView name);

// lowercase the string
-std::string ToLower(const std::string &class_name);
-std::string ToUpper(const std::string &class_name);
+std::string ToLower(compat::StringView class_name);
+std::string ToUpper(compat::StringView class_name);

// full_name() to lowercase with underscores
-std::string FullNameToLower(const std::string &full_name, const google::protobuf::FileDescriptor *file);
-std::string FullNameToUpper(const std::string &full_name, const google::protobuf::FileDescriptor *file);
+std::string FullNameToLower(compat::StringView full_name, const google::protobuf::FileDescriptor *file);
+std::string FullNameToUpper(compat::StringView full_name, const google::protobuf::FileDescriptor *file);

// full_name() to c-typename (with underscores for packages, otherwise camel case)
-std::string FullNameToC(const std::string &class_name, const google::protobuf::FileDescriptor *file);
+std::string FullNameToC(compat::StringView class_name, const google::protobuf::FileDescriptor *file);

// Splits, indents, formats, and prints comment lines
void PrintComment(google::protobuf::io::Printer* printer, std::string comment);

// make a string of spaces as long as input
-std::string ConvertToSpaces(const std::string &input);
+std::string ConvertToSpaces(compat::StringView input);

// Strips ".proto" or ".protodevel" from the end of a filename.
-std::string StripProto(const std::string& filename);
+std::string StripProto(compat::StringView filename);

// Get the C++ type name for a primitive type (e.g. "double", "::google::protobuf::int32", etc.).
// Note:  non-built-in type names will be qualified, meaning they will start
@@ -148,15 +149,14 @@ const char* PrimitiveTypeName(google::protobuf::FieldDescriptor::CppType type);
const char* DeclaredTypeMethodName(google::protobuf::FieldDescriptor::Type type);

// Convert a file name into a valid identifier.
-std::string FilenameIdentifier(const std::string& filename);
+std::string FilenameIdentifier(compat::StringView filename);

// return 'required', 'optional', or 'repeated'
std::string GetLabelName(google::protobuf::FieldDescriptor::Label label);

-
// write IntRanges entries for a bunch of sorted values.
// returns the number of ranges there are to bsearch.
-unsigned WriteIntRanges(google::protobuf::io::Printer* printer, int n_values, const int *values, const std::string &name);
+unsigned WriteIntRanges(google::protobuf::io::Printer* printer, int n_values, const int *values, compat::StringView name);

struct NameIndex
{

From 75f1c32cc429233a3726358c999009f9ea373b45 Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 20:25:43 -0500
Subject: [PATCH 05/11] Convert string views to owned strings where necessary

---
protoc-gen-c/c_enum.cc       | 2 +-
protoc-gen-c/c_enum_field.cc | 2 +-
protoc-gen-c/c_helpers.cc    | 8 ++++----
protoc-gen-c/c_helpers.h     | 3 +--
4 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/protoc-gen-c/c_enum.cc b/protoc-gen-c/c_enum.cc
index 9212ab82..311e4c86 100644
--- a/protoc-gen-c/c_enum.cc
+++ b/protoc-gen-c/c_enum.cc
@@ -152,7 +152,7 @@ void EnumGenerator::GenerateValueInitializer(google::protobuf::io::Printer *prin
    descriptor_->file()->options().optimize_for() ==
    google::protobuf::FileOptions_OptimizeMode_CODE_SIZE;
  vars["enum_value_name"] = vd->name();
-  vars["c_enum_value_name"] = FullNameToUpper(descriptor_->full_name(), descriptor_->file()) + "__" + vd->name();
+  vars["c_enum_value_name"] = FullNameToUpper(descriptor_->full_name(), descriptor_->file()) + "__" + std::string(vd->name());
  vars["value"] = SimpleItoa(vd->number());
  if (optimize_code_size)
    printer->Print(vars, "  { NULL, NULL, $value$ }, /* CODE_SIZE */\n");
diff --git a/protoc-gen-c/c_enum_field.cc b/protoc-gen-c/c_enum_field.cc
index 0926ae59..c3111f50 100644
--- a/protoc-gen-c/c_enum_field.cc
+++ b/protoc-gen-c/c_enum_field.cc
@@ -78,7 +78,7 @@ void SetEnumVariables(const google::protobuf::FieldDescriptor* descriptor,
  (*variables)["type"] = FullNameToC(descriptor->enum_type()->full_name(), descriptor->enum_type()->file());
  const google::protobuf::EnumValueDescriptor* default_value = descriptor->default_value_enum();
  (*variables)["default"] = FullNameToUpper(default_value->type()->full_name(), default_value->type()->file())
-                          + "__" + default_value->name();
+                          + "__" + std::string(default_value->name());
  (*variables)["deprecated"] = FieldDeprecated(descriptor);
}

diff --git a/protoc-gen-c/c_helpers.cc b/protoc-gen-c/c_helpers.cc
index c759c8c2..1aecef93 100644
--- a/protoc-gen-c/c_helpers.cc
+++ b/protoc-gen-c/c_helpers.cc
@@ -178,13 +178,13 @@ std::string ToCamel(compat::StringView name) {
std::string OverrideFullName(compat::StringView full_name, const google::protobuf::FileDescriptor* file) {
  const ProtobufCFileOptions opt = file->options().GetExtension(pb_c_file);
  if (!opt.has_c_package())
-    return full_name;
+    return std::string(full_name);

  std::string new_name = opt.c_package();
  if (file->package().empty())
    new_name += ".";

-  return new_name + full_name.substr(file->package().length());
+  return new_name + std::string(full_name.substr(file->package().length()));
}

std::string FullNameToLower(compat::StringView full_name, const google::protobuf::FileDescriptor* file) {
@@ -418,10 +418,10 @@ void SplitStringToIteratorUsing(compat::StringView full,
  while (begin_index != std::string::npos) {
    end_index = full.find_first_of(delim, begin_index);
    if (end_index == std::string::npos) {
-      *result++ = full.substr(begin_index);
+      *result++ = std::string(full.substr(begin_index));
      return;
    }
-    *result++ = full.substr(begin_index, (end_index - begin_index));
+    *result++ = std::string(full.substr(begin_index, (end_index - begin_index)));
    begin_index = full.find_first_not_of(delim, end_index);
  }
}
diff --git a/protoc-gen-c/c_helpers.h b/protoc-gen-c/c_helpers.h
index ccd39ca2..985e4db6 100644
--- a/protoc-gen-c/c_helpers.h
+++ b/protoc-gen-c/c_helpers.h
@@ -89,10 +89,9 @@ std::string SimpleDtoa(double f);
void SplitStringUsing(compat::StringView str, const char *delim, std::vector<std::string> *out);
std::string CEscape(compat::StringView src);
inline bool HasSuffixString(compat::StringView str, compat::StringView suffix) { return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; }
-inline std::string StripSuffixString(compat::StringView str, compat::StringView suffix) { if (HasSuffixString(str, suffix)) { return str.substr(0, str.size() - suffix.size()); } else { return str; } }
+inline std::string StripSuffixString(compat::StringView str, compat::StringView suffix) { if (HasSuffixString(str, suffix)) { return std::string(str.substr(0, str.size() - suffix.size())); } else { return std::string(str); } }
char* FastHexToBuffer(int i, char* buffer);

-
// Get the (unqualified) name that should be used for this field in C code.
// The name is coerced to lower-case to emulate proto1 behavior.  People
// should be using lowercase-with-underscores style for proto field names

From 0edca93db369fb84f01cc0d4e3ee4cd6c2ad7f4f Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 20:39:11 -0500
Subject: [PATCH 06/11] Convert various uses of `const char *` to
`compat::StringView`

Also replace some uses of arrays manually allocated with new/delete with
uses of `std::vector`.
---
protoc-gen-c/c_enum.cc    | 35 ++++++++++++++++++-----------------
protoc-gen-c/c_helpers.cc |  2 +-
protoc-gen-c/c_helpers.h  |  2 +-
protoc-gen-c/c_message.cc | 28 ++++++++++++----------------
protoc-gen-c/c_service.cc | 19 +++++++++----------
5 files changed, 41 insertions(+), 45 deletions(-)

diff --git a/protoc-gen-c/c_enum.cc b/protoc-gen-c/c_enum.cc
index 311e4c86..c7839edd 100644
--- a/protoc-gen-c/c_enum.cc
+++ b/protoc-gen-c/c_enum.cc
@@ -142,7 +142,7 @@ struct ValueIndex
  int value;
  unsigned index;
  unsigned final_index;                /* index in uniqified array of values */
-  const char *name;
+  compat::StringView name;
};
void EnumGenerator::GenerateValueInitializer(google::protobuf::io::Printer *printer, int index)
{
@@ -176,7 +176,7 @@ static int compare_value_indices_by_name(const void *a, const void *b)
{
  const ValueIndex *vi_a = (const ValueIndex *) a;
  const ValueIndex *vi_b = (const ValueIndex *) b;
-  return strcmp (vi_a->name, vi_b->name);
+  return vi_a->name.compare(vi_b->name);
}

void EnumGenerator::GenerateEnumDescriptor(google::protobuf::io::Printer* printer) {
@@ -194,18 +194,20 @@ void EnumGenerator::GenerateEnumDescriptor(google::protobuf::io::Printer* printe

  // Sort by name and value, dropping duplicate values if they appear later.
  // TODO: use a c++ paradigm for this!
-  NameIndex *name_index = new NameIndex[descriptor_->value_count()];
-  ValueIndex *value_index = new ValueIndex[descriptor_->value_count()];
-  for (int j = 0; j < descriptor_->value_count(); j++) {
+  std::vector<ValueIndex> value_index;
+  for (unsigned j = 0; j < descriptor_->value_count(); j++) {
    const google::protobuf::EnumValueDescriptor *vd = descriptor_->value(j);
-    name_index[j].index = j;
-    name_index[j].name = vd->name().c_str();
-    value_index[j].index = j;
-    value_index[j].value = vd->number();
-    value_index[j].name = vd->name().c_str();
+    value_index.push_back({
+      .value = vd->number(),
+      .index = j,
+      .final_index = 0,
+      .name = vd->name(),
+    });
  }
-  qsort(value_index, descriptor_->value_count(),
-       sizeof(ValueIndex), compare_value_indices_by_value_then_index);
+  qsort(&value_index[0],
+        value_index.size(),
+        sizeof(ValueIndex),
+        compare_value_indices_by_value_then_index);

  // only record unique values
  int n_unique_values;
@@ -275,8 +277,10 @@ void EnumGenerator::GenerateEnumDescriptor(google::protobuf::io::Printer* printe
  vars["n_ranges"] = SimpleItoa(n_ranges);

  if (!optimize_code_size) {
-    qsort(value_index, descriptor_->value_count(),
-        sizeof(ValueIndex), compare_value_indices_by_name);
+    qsort(&value_index[0],
+          value_index.size(),
+          sizeof(ValueIndex),
+          compare_value_indices_by_name);
    printer->Print(vars,
        "static const ProtobufCEnumValueIndex $lcclassname$__enum_values_by_name[$value_count$] =\n"
        "{\n");
@@ -319,9 +323,6 @@ void EnumGenerator::GenerateEnumDescriptor(google::protobuf::io::Printer* printe
        "  NULL,NULL,NULL,NULL   /* reserved[1234] */\n"
        "};\n");
  }
-
-  delete[] value_index;
-  delete[] name_index;
}

}  // namespace protobuf_c
diff --git a/protoc-gen-c/c_helpers.cc b/protoc-gen-c/c_helpers.cc
index 1aecef93..dec9ce28 100644
--- a/protoc-gen-c/c_helpers.cc
+++ b/protoc-gen-c/c_helpers.cc
@@ -261,7 +261,7 @@ int compare_name_indices_by_name(const void *a, const void *b)
{
  const NameIndex *ni_a = (const NameIndex *) a;
  const NameIndex *ni_b = (const NameIndex *) b;
-  return strcmp (ni_a->name, ni_b->name);
+  return ni_a->name.compare(ni_b->name);
}

std::string CEscape(compat::StringView src);
diff --git a/protoc-gen-c/c_helpers.h b/protoc-gen-c/c_helpers.h
index 985e4db6..69369997 100644
--- a/protoc-gen-c/c_helpers.h
+++ b/protoc-gen-c/c_helpers.h
@@ -160,7 +160,7 @@ unsigned WriteIntRanges(google::protobuf::io::Printer* printer, int n_values, co
struct NameIndex
{
  unsigned index;
-  const char *name;
+  compat::StringView name;
};
int compare_name_indices_by_name(const void*, const void*);

diff --git a/protoc-gen-c/c_message.cc b/protoc-gen-c/c_message.cc
index 46413873..7252923c 100644
--- a/protoc-gen-c/c_message.cc
+++ b/protoc-gen-c/c_message.cc
@@ -567,27 +567,26 @@ GenerateMessageDescriptor(google::protobuf::io::Printer* printer, bool gen_init)
       "static const ProtobufCFieldDescriptor $lcclassname$__field_descriptors[$n_fields$] =\n"
       "{\n");
  printer->Indent();
-  const google::protobuf::FieldDescriptor **sorted_fields = new const google::protobuf::FieldDescriptor *[descriptor_->field_count()];
+
+  std::vector<const google::protobuf::FieldDescriptor*> sorted_fields;
  for (int i = 0; i < descriptor_->field_count(); i++) {
-    sorted_fields[i] = descriptor_->field(i);
+    sorted_fields.push_back(descriptor_->field(i));
  }
-  qsort (sorted_fields, descriptor_->field_count(),
+  qsort(&sorted_fields[0], sorted_fields.size(),
       sizeof(const google::protobuf::FieldDescriptor*),
       compare_pfields_by_number);
-  for (int i = 0; i < descriptor_->field_count(); i++) {
-    const google::protobuf::FieldDescriptor* field = sorted_fields[i];
+  for (auto field : sorted_fields) {
    field_generators_.get(field).GenerateDescriptorInitializer(printer);
  }
  printer->Outdent();
  printer->Print(vars, "};\n");

  if (!optimize_code_size) {
-    NameIndex *field_indices = new NameIndex [descriptor_->field_count()];
-    for (int i = 0; i < descriptor_->field_count(); i++) {
-      field_indices[i].name = sorted_fields[i]->name().c_str();
-      field_indices[i].index = i;
+    std::vector<NameIndex> field_indices;
+    for (unsigned i = 0; i < descriptor_->field_count(); i++) {
+      field_indices.push_back({ .index = i, .name = sorted_fields[i]->name() });
    }
-    qsort (field_indices, descriptor_->field_count(), sizeof (NameIndex),
+    qsort(&field_indices[0], field_indices.size(), sizeof(NameIndex),
        compare_name_indices_by_name);
    printer->Print(vars, "static const unsigned $lcclassname$__field_indices_by_name[] = {\n");
    for (int i = 0; i < descriptor_->field_count(); i++) {
@@ -596,19 +595,16 @@ GenerateMessageDescriptor(google::protobuf::io::Printer* printer, bool gen_init)
      printer->Print(vars, "  $index$,   /* field[$index$] = $name$ */\n");
    }
    printer->Print("};\n");
-    delete[] field_indices;
  }

  // create range initializers
-  int *values = new int[descriptor_->field_count()];
+  std::vector<int> values;
  for (int i = 0; i < descriptor_->field_count(); i++) {
-    values[i] = sorted_fields[i]->number();
+    values.push_back(sorted_fields[i]->number());
  }
  int n_ranges = WriteIntRanges(printer,
-                               descriptor_->field_count(), values,
+                               descriptor_->field_count(), &values[0],
                               vars["lcclassname"] + "__number_ranges");
-  delete [] values;
-  delete [] sorted_fields;

  vars["n_ranges"] = SimpleItoa(n_ranges);
    } else {
diff --git a/protoc-gen-c/c_service.cc b/protoc-gen-c/c_service.cc
index ee4d4a95..2c3ddcf3 100644
--- a/protoc-gen-c/c_service.cc
+++ b/protoc-gen-c/c_service.cc
@@ -184,19 +184,19 @@ void ServiceGenerator::GenerateInit(google::protobuf::io::Printer* printer)
                "}\n");
}

-struct MethodIndexAndName { unsigned i; const char *name; };
+struct MethodIndexAndName { unsigned i; compat::StringView name; };
static int
compare_method_index_and_name_by_name (const void *a, const void *b)
{
  const MethodIndexAndName *ma = (const MethodIndexAndName *) a;
  const MethodIndexAndName *mb = (const MethodIndexAndName *) b;
-  return strcmp (ma->name, mb->name);
+  return ma->name.compare(mb->name);
}

void ServiceGenerator::GenerateServiceDescriptor(google::protobuf::io::Printer* printer)
{
  int n_methods = descriptor_->method_count();
-  MethodIndexAndName *mi_array = new MethodIndexAndName[n_methods];
+  std::vector<MethodIndexAndName> mi_array;

  bool optimize_code_size = descriptor_->file()->options().has_optimize_for() &&
    descriptor_->file()->options().optimize_for() ==
@@ -205,7 +205,7 @@ void ServiceGenerator::GenerateServiceDescriptor(google::protobuf::io::Printer*
  vars_["n_methods"] = SimpleItoa(n_methods);
  printer->Print(vars_, "static const ProtobufCMethodDescriptor $lcfullname$__method_descriptors[$n_methods$] =\n"
                       "{\n");
-  for (int i = 0; i < n_methods; i++) {
+  for (unsigned i = 0; i < n_methods; i++) {
    const google::protobuf::MethodDescriptor* method = descriptor_->method(i);
    vars_["method"] = method->name();
    vars_["input_descriptor"] = "&" + FullNameToLower(method->input_type()->full_name(), method->input_type()->file()) + "__descriptor";
@@ -217,14 +217,15 @@ void ServiceGenerator::GenerateServiceDescriptor(google::protobuf::io::Printer*
      printer->Print(vars_,
          "  { \"$method$\", $input_descriptor$, $output_descriptor$ },\n");
    }
-    mi_array[i].i = i;
-    mi_array[i].name = method->name().c_str();
+    mi_array.push_back({i, method->name()});
  }
  printer->Print(vars_, "};\n");

  if (!optimize_code_size) {
-    qsort ((void*)mi_array, n_methods, sizeof (MethodIndexAndName),
-        compare_method_index_and_name_by_name);
+    qsort(&mi_array[0],
+          mi_array.size(),
+          sizeof(MethodIndexAndName),
+          compare_method_index_and_name_by_name);
    printer->Print(vars_, "const unsigned $lcfullname$__method_indices_by_name[] = {\n");
    for (int i = 0; i < n_methods; i++) {
      vars_["i"] = SimpleItoa(mi_array[i].i);
@@ -258,8 +259,6 @@ void ServiceGenerator::GenerateServiceDescriptor(google::protobuf::io::Printer*
        "  $lcfullname$__method_indices_by_name\n"
        "};\n");
  }
-
-  delete[] mi_array;
}

void ServiceGenerator::GenerateCallersImplementations(google::protobuf::io::Printer* printer)

From ebeddac1a746393a16d9ba4cf80e3d12c3ab7d7f Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 20:56:48 -0500
Subject: [PATCH 07/11] Fix indentation of
MessageGenerator::GenerateMessageDescriptor()

---
protoc-gen-c/c_message.cc | 321 +++++++++++++++++++-------------------
1 file changed, 159 insertions(+), 162 deletions(-)

diff --git a/protoc-gen-c/c_message.cc b/protoc-gen-c/c_message.cc
index 7252923c..2a3b2a2f 100644
--- a/protoc-gen-c/c_message.cc
+++ b/protoc-gen-c/c_message.cc
@@ -461,199 +461,196 @@ GenerateHelperFunctionDefinitions(google::protobuf::io::Printer* printer,

void MessageGenerator::
GenerateMessageDescriptor(google::protobuf::io::Printer* printer, bool gen_init) {
-    std::map<std::string, std::string> vars;
-    vars["fullname"] = descriptor_->full_name();
-    vars["classname"] = FullNameToC(descriptor_->full_name(), descriptor_->file());
-    vars["lcclassname"] = FullNameToLower(descriptor_->full_name(), descriptor_->file());
-    vars["shortname"] = ToCamel(descriptor_->name());
-    vars["n_fields"] = SimpleItoa(descriptor_->field_count());
-    vars["packagename"] = descriptor_->file()->package();
-
-    bool optimize_code_size = descriptor_->file()->options().has_optimize_for() &&
-        descriptor_->file()->options().optimize_for() ==
-        google::protobuf::FileOptions_OptimizeMode_CODE_SIZE;
-
-    const ProtobufCMessageOptions opt =
-           descriptor_->options().GetExtension(pb_c_msg);
-    // Override parent settings, if needed
-    if (opt.has_gen_init_helpers())
-      gen_init = opt.gen_init_helpers();
-
-    for (int i = 0; i < descriptor_->nested_type_count(); i++) {
-      nested_generators_[i]->GenerateMessageDescriptor(printer, gen_init);
-    }
+  std::map<std::string, std::string> vars;
+  vars["fullname"] = descriptor_->full_name();
+  vars["classname"] = FullNameToC(descriptor_->full_name(), descriptor_->file());
+  vars["lcclassname"] = FullNameToLower(descriptor_->full_name(), descriptor_->file());
+  vars["shortname"] = ToCamel(descriptor_->name());
+  vars["n_fields"] = SimpleItoa(descriptor_->field_count());
+  vars["packagename"] = descriptor_->file()->package();

-    for (int i = 0; i < descriptor_->enum_type_count(); i++) {
-      enum_generators_[i]->GenerateEnumDescriptor(printer);
-    }
+  bool optimize_code_size = descriptor_->file()->options().has_optimize_for() &&
+    descriptor_->file()->options().optimize_for() ==
+      google::protobuf::FileOptions_OptimizeMode_CODE_SIZE;

-    for (int i = 0; i < descriptor_->field_count(); i++) {
-      const google::protobuf::FieldDescriptor* fd = descriptor_->field(i);
-      if (fd->has_default_value()) {
-       field_generators_.get(fd).GenerateDefaultValueImplementations(printer);
-      }
-    }
+  const ProtobufCMessageOptions opt = descriptor_->options().GetExtension(pb_c_msg);
+  // Override parent settings, if needed
+  if (opt.has_gen_init_helpers()) {
+    gen_init = opt.gen_init_helpers();
+  }

-    for (int i = 0; i < descriptor_->field_count(); i++) {
-      const google::protobuf::FieldDescriptor* fd = descriptor_->field(i);
-      const ProtobufCFieldOptions opt = fd->options().GetExtension(pb_c_field);
-      if (fd->has_default_value()) {
-
-       bool already_defined = false;
-       vars["name"] = fd->name();
-       vars["lcname"] = CamelToLower(fd->name());
-       vars["maybe_static"] = "static ";
-       vars["field_dv_ctype_suffix"] = "";
-       vars["default_value"] = field_generators_.get(fd).GetDefaultValue();
-       switch (fd->cpp_type()) {
-       case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
-         vars["field_dv_ctype"] = "int32_t";
-         break;
-       case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
-         vars["field_dv_ctype"] = "int64_t";
-         break;
-       case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
-         vars["field_dv_ctype"] = "uint32_t";
-         break;
-       case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
-         vars["field_dv_ctype"] = "uint64_t";
-         break;
-       case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
-         vars["field_dv_ctype"] = "float";
-         break;
-       case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
-         vars["field_dv_ctype"] = "double";
-         break;
-       case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
-         vars["field_dv_ctype"] = "protobuf_c_boolean";
-         break;
-
-       case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
-         // NOTE: not supported by protobuf
-         vars["maybe_static"] = "";
-         vars["field_dv_ctype"] = "{ ... }";
-         GOOGLE_LOG(FATAL) << "Messages can't have default values!";
-         break;
-       case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
-         if (fd->type() == google::protobuf::FieldDescriptor::TYPE_BYTES || opt.string_as_bytes())
-         {
-           vars["field_dv_ctype"] = "ProtobufCBinaryData";
-         }
-         else   /* STRING type */
-         {
-           already_defined = true;
-           vars["maybe_static"] = "";
-           vars["field_dv_ctype"] = "char";
-           vars["field_dv_ctype_suffix"] = "[]";
-         }
-         break;
-       case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
-         {
-           const google::protobuf::EnumValueDescriptor* vd = fd->default_value_enum();
-           vars["field_dv_ctype"] = FullNameToC(vd->type()->full_name(), vd->type()->file());
-           break;
-         }
-       default:
-         GOOGLE_LOG(FATAL) << "Unknown CPPTYPE";
-         break;
-       }
-       if (!already_defined)
-         printer->Print(vars, "$maybe_static$const $field_dv_ctype$ $lcclassname$__$lcname$__default_value$field_dv_ctype_suffix$ = $default_value$;\n");
-      }
-    }
+  for (int i = 0; i < descriptor_->nested_type_count(); i++) {
+    nested_generators_[i]->GenerateMessageDescriptor(printer, gen_init);
+  }

-    if ( descriptor_->field_count() ) {
-  printer->Print(vars,
-       "static const ProtobufCFieldDescriptor $lcclassname$__field_descriptors[$n_fields$] =\n"
-       "{\n");
-  printer->Indent();
+  for (int i = 0; i < descriptor_->enum_type_count(); i++) {
+    enum_generators_[i]->GenerateEnumDescriptor(printer);
+  }

-  std::vector<const google::protobuf::FieldDescriptor*> sorted_fields;
  for (int i = 0; i < descriptor_->field_count(); i++) {
-    sorted_fields.push_back(descriptor_->field(i));
-  }
-  qsort(&sorted_fields[0], sorted_fields.size(),
-       sizeof(const google::protobuf::FieldDescriptor*),
-       compare_pfields_by_number);
-  for (auto field : sorted_fields) {
-    field_generators_.get(field).GenerateDescriptorInitializer(printer);
+    const google::protobuf::FieldDescriptor* fd = descriptor_->field(i);
+    if (fd->has_default_value()) {
+      field_generators_.get(fd).GenerateDefaultValueImplementations(printer);
+    }
  }
-  printer->Outdent();
-  printer->Print(vars, "};\n");

-  if (!optimize_code_size) {
-    std::vector<NameIndex> field_indices;
-    for (unsigned i = 0; i < descriptor_->field_count(); i++) {
-      field_indices.push_back({ .index = i, .name = sorted_fields[i]->name() });
+  for (int i = 0; i < descriptor_->field_count(); i++) {
+    const google::protobuf::FieldDescriptor* fd = descriptor_->field(i);
+    const ProtobufCFieldOptions opt = fd->options().GetExtension(pb_c_field);
+    if (fd->has_default_value()) {
+      bool already_defined = false;
+      vars["name"] = fd->name();
+      vars["lcname"] = CamelToLower(fd->name());
+      vars["maybe_static"] = "static ";
+      vars["field_dv_ctype_suffix"] = "";
+      vars["default_value"] = field_generators_.get(fd).GetDefaultValue();
+      switch (fd->cpp_type()) {
+      case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
+        vars["field_dv_ctype"] = "int32_t";
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
+        vars["field_dv_ctype"] = "int64_t";
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
+        vars["field_dv_ctype"] = "uint32_t";
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
+        vars["field_dv_ctype"] = "uint64_t";
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
+        vars["field_dv_ctype"] = "float";
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
+        vars["field_dv_ctype"] = "double";
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
+        vars["field_dv_ctype"] = "protobuf_c_boolean";
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
+        // NOTE: not supported by protobuf
+        vars["maybe_static"] = "";
+        vars["field_dv_ctype"] = "{ ... }";
+        GOOGLE_LOG(FATAL) << "Messages can't have default values!";
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
+        if (fd->type() == google::protobuf::FieldDescriptor::TYPE_BYTES || opt.string_as_bytes()) {
+          vars["field_dv_ctype"] = "ProtobufCBinaryData";
+        } else {
+          /* STRING type */
+          already_defined = true;
+          vars["maybe_static"] = "";
+          vars["field_dv_ctype"] = "char";
+          vars["field_dv_ctype_suffix"] = "[]";
+        }
+        break;
+      case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
+        const google::protobuf::EnumValueDescriptor* vd = fd->default_value_enum();
+        vars["field_dv_ctype"] = FullNameToC(vd->type()->full_name(), vd->type()->file());
+        break;
+      }
+      default:
+        GOOGLE_LOG(FATAL) << "Unknown CPPTYPE";
+        break;
+      }
+      if (!already_defined) {
+        printer->Print(vars, "$maybe_static$const $field_dv_ctype$ $lcclassname$__$lcname$__default_value$field_dv_ctype_suffix$ = $default_value$;\n");
+      }
    }
-    qsort(&field_indices[0], field_indices.size(), sizeof(NameIndex),
-        compare_name_indices_by_name);
-    printer->Print(vars, "static const unsigned $lcclassname$__field_indices_by_name[] = {\n");
+  }
+
+  if (descriptor_->field_count()) {
+    printer->Print(vars,
+      "static const ProtobufCFieldDescriptor $lcclassname$__field_descriptors[$n_fields$] =\n"
+      "{\n");
+    printer->Indent();
+
+    std::vector<const google::protobuf::FieldDescriptor*> sorted_fields;
    for (int i = 0; i < descriptor_->field_count(); i++) {
-      vars["index"] = SimpleItoa(field_indices[i].index);
-      vars["name"] = field_indices[i].name;
-      printer->Print(vars, "  $index$,   /* field[$index$] = $name$ */\n");
+      sorted_fields.push_back(descriptor_->field(i));
    }
-    printer->Print("};\n");
-  }
+    qsort(&sorted_fields[0],
+          sorted_fields.size(),
+          sizeof(const google::protobuf::FieldDescriptor*),
+          compare_pfields_by_number);
+    for (auto field : sorted_fields) {
+      field_generators_.get(field).GenerateDescriptorInitializer(printer);
+    }
+    printer->Outdent();
+    printer->Print(vars, "};\n");

-  // create range initializers
-  std::vector<int> values;
-  for (int i = 0; i < descriptor_->field_count(); i++) {
-    values.push_back(sorted_fields[i]->number());
-  }
-  int n_ranges = WriteIntRanges(printer,
-                               descriptor_->field_count(), &values[0],
-                               vars["lcclassname"] + "__number_ranges");
+    if (!optimize_code_size) {
+      std::vector<NameIndex> field_indices;
+      for (unsigned i = 0; i < descriptor_->field_count(); i++) {
+        field_indices.push_back({ .index = i, .name = sorted_fields[i]->name() });
+      }
+      qsort(&field_indices[0],
+            field_indices.size(),
+            sizeof(NameIndex),
+            compare_name_indices_by_name);
+      printer->Print(vars, "static const unsigned $lcclassname$__field_indices_by_name[] = {\n");
+      for (int i = 0; i < descriptor_->field_count(); i++) {
+        vars["index"] = SimpleItoa(field_indices[i].index);
+        vars["name"] = field_indices[i].name;
+        printer->Print(vars, "  $index$,   /* field[$index$] = $name$ */\n");
+      }
+      printer->Print("};\n");
+    }

-  vars["n_ranges"] = SimpleItoa(n_ranges);
-    } else {
-      /* MS compiler can't handle arrays with zero size and empty
-       * initialization list. Furthermore it is an extension of GCC only but
-       * not a standard. */
-      vars["n_ranges"] = "0";
-  printer->Print(vars,
-        "#define $lcclassname$__field_descriptors NULL\n"
-        "#define $lcclassname$__field_indices_by_name NULL\n"
-        "#define $lcclassname$__number_ranges NULL\n");
+    // create range initializers
+    std::vector<int> values;
+    for (int i = 0; i < descriptor_->field_count(); i++) {
+      values.push_back(sorted_fields[i]->number());
    }
+    int n_ranges = WriteIntRanges(printer,
+                                  descriptor_->field_count(),
+                                  &values[0],
+                                  vars["lcclassname"] + "__number_ranges");
+
+    vars["n_ranges"] = SimpleItoa(n_ranges);
+  } else {
+    /* MS compiler can't handle arrays with zero size and empty
+     * initialization list. Furthermore it is an extension of GCC only but
+     * not a standard. */
+    vars["n_ranges"] = "0";
+    printer->Print(vars,
+      "#define $lcclassname$__field_descriptors NULL\n"
+      "#define $lcclassname$__field_indices_by_name NULL\n"
+      "#define $lcclassname$__number_ranges NULL\n");
+  }

  printer->Print(vars,
-      "const ProtobufCMessageDescriptor $lcclassname$__descriptor =\n"
-      "{\n"
-      "  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n");
+    "const ProtobufCMessageDescriptor $lcclassname$__descriptor =\n"
+    "{\n"
+    "  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n");
  if (optimize_code_size) {
    printer->Print("  NULL,NULL,NULL,NULL, /* CODE_SIZE */\n");
  } else {
    printer->Print(vars,
-        "  \"$fullname$\",\n"
-        "  \"$shortname$\",\n"
-        "  \"$classname$\",\n"
-        "  \"$packagename$\",\n");
+      "  \"$fullname$\",\n"
+      "  \"$shortname$\",\n"
+      "  \"$classname$\",\n"
+      "  \"$packagename$\",\n");
  }
  printer->Print(vars,
-      "  sizeof($classname$),\n"
-      "  $n_fields$,\n"
-      "  $lcclassname$__field_descriptors,\n");
+    "  sizeof($classname$),\n"
+    "  $n_fields$,\n"
+    "  $lcclassname$__field_descriptors,\n");
  if (optimize_code_size) {
    printer->Print("  NULL, /* CODE_SIZE */\n");
  } else {
-    printer->Print(vars,
-        "  $lcclassname$__field_indices_by_name,\n");
+    printer->Print(vars, "  $lcclassname$__field_indices_by_name,\n");
  }
  printer->Print(vars,
-      "  $n_ranges$,"
-      "  $lcclassname$__number_ranges,\n");
+    "  $n_ranges$,"
+    "  $lcclassname$__number_ranges,\n");
  if (gen_init) {
-    printer->Print(vars,
-      "  (ProtobufCMessageInit) $lcclassname$__init,\n");
+    printer->Print(vars, "  (ProtobufCMessageInit) $lcclassname$__init,\n");
  } else {
-    printer->Print(vars,
-      "  NULL, /* gen_init_helpers = false */\n");
+    printer->Print(vars, "  NULL, /* gen_init_helpers = false */\n");
  }
  printer->Print(vars,
-      "  NULL,NULL,NULL    /* reserved[123] */\n"
-      "};\n");
+    "  NULL,NULL,NULL    /* reserved[123] */\n"
+    "};\n");
}

int MessageGenerator::GetOneofUnionOrder(const google::protobuf::FieldDescriptor* fd)

From c59b146aee2d97091ca2adeecd3f2741cb7f0082 Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 21:10:37 -0500
Subject: [PATCH 08/11] compat: Use absl::string_view instead of
google::protobuf::internal::DescriptorStringView

Even though google::protobuf::internal::DescriptorStringView is exposed
in public protobuf headers, it's probably not a good idea to rely on an
"internal" typedef.

According to https://protobuf.dev/news/2024-10-02/#descriptor-apis:

   v30 will update return types in descriptor (such as full_name) to be
   absl::string_view.

So `absl::string_view` is probably the right type to use here.
---
protoc-gen-c/compat.h | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/protoc-gen-c/compat.h b/protoc-gen-c/compat.h
index fe8041b5..a70cef34 100644
--- a/protoc-gen-c/compat.h
+++ b/protoc-gen-c/compat.h
@@ -37,12 +37,16 @@
# define GOOGLE_LOG            ABSL_LOG
#endif

+#if GOOGLE_PROTOBUF_VERSION >= 6030000
+# include <absl/strings/string_view.h>
+#endif
+
namespace protobuf_c {

namespace compat {

#if GOOGLE_PROTOBUF_VERSION >= 6030000
-typedef google::protobuf::internal::DescriptorStringView StringView;
+typedef absl::string_view StringView;
#else
typedef const std::string& StringView;
#endif

From 9c56038fd9d3cc2552c297457d7a66efe5cbd2c7 Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 21:37:30 -0500
Subject: [PATCH 09/11] Makefile.am: Add compat.h to
protoc_gen_c_protoc_gen_c_SOURCES

---
Makefile.am | 1 +
1 file changed, 1 insertion(+)

diff --git a/Makefile.am b/Makefile.am
index 77aa9d99..26d19f16 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -102,6 +102,7 @@ protoc_gen_c_protoc_gen_c_SOURCES = \
       protoc-gen-c/c_service.h \
       protoc-gen-c/c_string_field.cc \
       protoc-gen-c/c_string_field.h \
+       protoc-gen-c/compat.h \
       protobuf-c/protobuf-c.pb.cc \
       protobuf-c/protobuf-c.pb.h \
       protoc-gen-c/main.cc

From 4ebd5cd8238d1f2ac6291b8c8925f34e16ce2123 Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 21:38:07 -0500
Subject: [PATCH 10/11] compat: Conditionalize the include of <string>

It is only needed on older protobuf versions where absl::string_view is
not being used.
---
protoc-gen-c/compat.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/protoc-gen-c/compat.h b/protoc-gen-c/compat.h
index a70cef34..f6ace7cb 100644
--- a/protoc-gen-c/compat.h
+++ b/protoc-gen-c/compat.h
@@ -28,8 +28,6 @@
#ifndef PROTOBUF_C_PROTOC_GEN_C_COMPAT_H__
#define PROTOBUF_C_PROTOC_GEN_C_COMPAT_H__

-#include <string>
-
#if GOOGLE_PROTOBUF_VERSION >= 4022000
# define GOOGLE_ARRAYSIZE      ABSL_ARRAYSIZE
# define GOOGLE_CHECK_EQ       ABSL_CHECK_EQ
@@ -39,6 +37,8 @@

#if GOOGLE_PROTOBUF_VERSION >= 6030000
# include <absl/strings/string_view.h>
+#else
+# include <string>
#endif

namespace protobuf_c {

From 9a6b35e1e6956fb5cb044910448049b7a5339244 Mon Sep 17 00:00:00 2001
From: Robert Edmonds <[email protected]>
Date: Sat, 8 Feb 2025 21:44:42 -0500
Subject: [PATCH 11/11] Cater to Microsoft Visual C++

Apparently MSVC doesn't support designated initializers for some reason.
---
protoc-gen-c/c_enum.cc    | 9 ++-------
protoc-gen-c/c_message.cc | 2 +-
2 files changed, 3 insertions(+), 8 deletions(-)

diff --git a/protoc-gen-c/c_enum.cc b/protoc-gen-c/c_enum.cc
index c7839edd..1940ba9d 100644
--- a/protoc-gen-c/c_enum.cc
+++ b/protoc-gen-c/c_enum.cc
@@ -195,14 +195,9 @@ void EnumGenerator::GenerateEnumDescriptor(google::protobuf::io::Printer* printe
  // Sort by name and value, dropping duplicate values if they appear later.
  // TODO: use a c++ paradigm for this!
  std::vector<ValueIndex> value_index;
-  for (unsigned j = 0; j < descriptor_->value_count(); j++) {
+  for (int j = 0; j < descriptor_->value_count(); j++) {
    const google::protobuf::EnumValueDescriptor *vd = descriptor_->value(j);
-    value_index.push_back({
-      .value = vd->number(),
-      .index = j,
-      .final_index = 0,
-      .name = vd->name(),
-    });
+    value_index.push_back({ vd->number(), (unsigned)j, 0, vd->name() });
  }
  qsort(&value_index[0],
        value_index.size(),
diff --git a/protoc-gen-c/c_message.cc b/protoc-gen-c/c_message.cc
index 2a3b2a2f..94889179 100644
--- a/protoc-gen-c/c_message.cc
+++ b/protoc-gen-c/c_message.cc
@@ -581,7 +581,7 @@ GenerateMessageDescriptor(google::protobuf::io::Printer* printer, bool gen_init)
    if (!optimize_code_size) {
      std::vector<NameIndex> field_indices;
      for (unsigned i = 0; i < descriptor_->field_count(); i++) {
-        field_indices.push_back({ .index = i, .name = sorted_fields[i]->name() });
+        field_indices.push_back({ i, sorted_fields[i]->name() });
      }
      qsort(&field_indices[0],
            field_indices.size(),