Use a helper function to implement get_all_foo_names functions.

Saves some duplicated logic.

Change-Id: I202fa92a88101f9ad735648bc414ab05752641da
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/59685
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/span.h b/include/openssl/span.h
index 67a1a5c..34b39c0 100644
--- a/include/openssl/span.h
+++ b/include/openssl/span.h
@@ -205,6 +205,11 @@
   return MakeConstSpan(c.data(), c.size());
 }
 
+template <typename T, size_t size>
+Span<const T> MakeConstSpan(T (&array)[size]) {
+  return array;
+}
+
 BSSL_NAMESPACE_END
 
 }  // extern C++
diff --git a/ssl/internal.h b/ssl/internal.h
index 1e6da21..2f8ce57 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -451,6 +451,27 @@
 // CBBFinishArray behaves like |CBB_finish| but stores the result in an Array.
 OPENSSL_EXPORT bool CBBFinishArray(CBB *cbb, Array<uint8_t> *out);
 
+// GetAllNames helps to implement |*_get_all_*_names| style functions. It
+// writes at most |max_out| string pointers to |out| and returns the number that
+// it would have liked to have written. The strings written consist of
+// |fixed_names_len| strings from |fixed_names| followed by |objects_len|
+// strings taken by projecting |objects| through |name|.
+template <typename T, typename Name>
+inline size_t GetAllNames(const char **out, size_t max_out,
+                          Span<const char *const> fixed_names, Name(T::*name),
+                          Span<const T> objects) {
+  auto span = bssl::MakeSpan(out, max_out);
+  for (size_t i = 0; !span.empty() && i < fixed_names.size(); i++) {
+    span[0] = fixed_names[i];
+    span = span.subspan(1);
+  }
+  span = span.subspan(0, objects.size());
+  for (size_t i = 0; i < span.size(); i++) {
+    span[i] = objects[i].*name;
+  }
+  return fixed_names.size() + objects.size();
+}
+
 
 // Protocol versions.
 //
diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc
index ebb0753..abf5e3d 100644
--- a/ssl/ssl_cipher.cc
+++ b/ssl/ssl_cipher.cc
@@ -1477,13 +1477,15 @@
   return TLS1_2_VERSION;
 }
 
+static const char* kUnknownCipher = "(NONE)";
+
 // return the actual cipher being used
 const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher) {
   if (cipher != NULL) {
     return cipher->name;
   }
 
-  return "(NONE)";
+  return kUnknownCipher;
 }
 
 const char *SSL_CIPHER_standard_name(const SSL_CIPHER *cipher) {
@@ -1696,23 +1698,11 @@
 void SSL_COMP_free_compression_methods(void) {}
 
 size_t SSL_get_all_cipher_names(const char **out, size_t max_out) {
-  auto span = MakeSpan(out, max_out);
-  if (!span.empty()) {
-    // |SSL_CIPHER_get_name| returns "(NONE)" for null.
-    span[0] = "(NONE)";
-    span = span.subspan(1);
-  }
-  span = span.subspan(0, OPENSSL_ARRAY_SIZE(kCiphers));
-  for (size_t i = 0; i < span.size(); i++) {
-    span[i] = kCiphers[i].name;
-  }
-  return 1 + OPENSSL_ARRAY_SIZE(kCiphers);
+  return GetAllNames(out, max_out, MakeConstSpan(&kUnknownCipher, 1),
+                     &SSL_CIPHER::name, MakeConstSpan(kCiphers));
 }
 
 size_t SSL_get_all_standard_cipher_names(const char **out, size_t max_out) {
-  auto span = MakeSpan(out, max_out).subspan(0, OPENSSL_ARRAY_SIZE(kCiphers));
-  for (size_t i = 0; i < span.size(); i++) {
-    span[i] = kCiphers[i].standard_name;
-  }
-  return OPENSSL_ARRAY_SIZE(kCiphers);
+  return GetAllNames(out, max_out, Span<const char *>(),
+                     &SSL_CIPHER::standard_name, MakeConstSpan(kCiphers));
 }
diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc
index 09a9ad3..77f16b5 100644
--- a/ssl/ssl_key_share.cc
+++ b/ssl/ssl_key_share.cc
@@ -359,10 +359,6 @@
 }
 
 size_t SSL_get_all_curve_names(const char **out, size_t max_out) {
-  auto span =
-      MakeSpan(out, max_out).subspan(0, OPENSSL_ARRAY_SIZE(kNamedGroups));
-  for (size_t i = 0; i < span.size(); i++) {
-    span[i] = kNamedGroups[i].name;
-  }
-  return OPENSSL_ARRAY_SIZE(kNamedGroups);
+  return GetAllNames(out, max_out, Span<const char *>(), &NamedGroup::name,
+                     MakeConstSpan(kNamedGroups));
 }
diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc
index 46bef32..57116cd 100644
--- a/ssl/ssl_privkey.cc
+++ b/ssl/ssl_privkey.cc
@@ -485,12 +485,14 @@
 
 static constexpr size_t kMaxSignatureAlgorithmNameLen = 23;
 
-// This was "constexpr" rather than "const", but that triggered a bug in MSVC
-// where it didn't pad the strings to the correct length.
-static const struct {
+struct SignatureAlgorithmName {
   uint16_t signature_algorithm;
   const char name[kMaxSignatureAlgorithmNameLen];
-} kSignatureAlgorithmNames[] = {
+};
+
+// This was "constexpr" rather than "const", but that triggered a bug in MSVC
+// where it didn't pad the strings to the correct length.
+static const SignatureAlgorithmName kSignatureAlgorithmNames[] = {
     {SSL_SIGN_RSA_PKCS1_MD5_SHA1, "rsa_pkcs1_md5_sha1"},
     {SSL_SIGN_RSA_PKCS1_SHA1, "rsa_pkcs1_sha1"},
     {SSL_SIGN_RSA_PKCS1_SHA256, "rsa_pkcs1_sha256"},
@@ -516,6 +518,8 @@
         return "ecdsa_sha384";
       case SSL_SIGN_ECDSA_SECP521R1_SHA512:
         return "ecdsa_sha512";
+        // If adding more here, also update
+        // |SSL_get_all_signature_algorithm_names|.
     }
   }
 
@@ -529,24 +533,11 @@
 }
 
 size_t SSL_get_all_signature_algorithm_names(const char **out, size_t max_out) {
-  auto span = MakeSpan(out, max_out);
-  if (!span.empty()) {
-    span[0] = "ecdsa_sha256";
-    span = span.subspan(1);
-  }
-  if (!span.empty()) {
-    span[0] = "ecdsa_sha384";
-    span = span.subspan(1);
-  }
-  if (!span.empty()) {
-    span[0] = "ecdsa_sha512";
-    span = span.subspan(1);
-  }
-  span = span.subspan(0, OPENSSL_ARRAY_SIZE(kSignatureAlgorithmNames));
-  for (size_t i = 0; i < span.size(); i++) {
-    span[i] = kSignatureAlgorithmNames[i].name;
-  }
-  return 3 + OPENSSL_ARRAY_SIZE(kSignatureAlgorithmNames);
+  const char *kPredefinedNames[] = {"ecdsa_sha256", "ecdsa_sha384",
+                                    "ecdsa_sha512"};
+  return GetAllNames(out, max_out, MakeConstSpan(kPredefinedNames),
+                     &SignatureAlgorithmName::name,
+                     MakeConstSpan(kSignatureAlgorithmNames));
 }
 
 int SSL_get_signature_algorithm_key_type(uint16_t sigalg) {
diff --git a/ssl/ssl_versions.cc b/ssl/ssl_versions.cc
index a836606..db298fb 100644
--- a/ssl/ssl_versions.cc
+++ b/ssl/ssl_versions.cc
@@ -85,10 +85,14 @@
 // The following functions map between API versions and wire versions. The
 // public API works on wire versions.
 
-static const struct {
+static const char* kUnknownVersion = "unknown";
+
+struct VersionInfo {
   uint16_t version;
   const char *name;
-} kVersionNames[] = {
+};
+
+static const VersionInfo kVersionNames[] = {
     {TLS1_3_VERSION, "TLSv1.3"},
     {TLS1_2_VERSION, "TLSv1.2"},
     {TLS1_1_VERSION, "TLSv1.1"},
@@ -103,7 +107,7 @@
       return v.name;
     }
   }
-  return "unknown";
+  return kUnknownVersion;
 }
 
 static uint16_t wire_version_to_api(uint16_t version) {
@@ -383,17 +387,8 @@
 }
 
 size_t SSL_get_all_version_names(const char **out, size_t max_out) {
-  auto span = MakeSpan(out, max_out);
-  if (!span.empty()) {
-    // |ssl_version_to_string| returns "unknown" for unknown versions.
-    span[0] = "unknown";
-    span = span.subspan(1);
-  }
-  span = span.subspan(0, OPENSSL_ARRAY_SIZE(kVersionNames));
-  for (size_t i = 0; i < span.size(); i++) {
-    span[i] = kVersionNames[i].name;
-  }
-  return 1 + OPENSSL_ARRAY_SIZE(kVersionNames);
+  return GetAllNames(out, max_out, MakeConstSpan(&kUnknownVersion, 1),
+                     &VersionInfo::name, MakeConstSpan(kVersionNames));
 }
 
 const char *SSL_SESSION_get_version(const SSL_SESSION *session) {