Add a helper function to allow ErrorStr not using static char array (#36391)

* Add a helper function to allow ErrorStr does not rely on static char
array

* Use dedicated storage for ErrorStr and add unit tests

* Restyled by whitespace

* Restyled by clang-format

* remove size()

---------

Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/src/lib/core/ErrorStr.cpp b/src/lib/core/ErrorStr.cpp
index 6d83bc0..b3cf968 100644
--- a/src/lib/core/ErrorStr.cpp
+++ b/src/lib/core/ErrorStr.cpp
@@ -29,7 +29,7 @@
 /**
  * Static buffer to store the formatted error string.
  */
-static char sErrorStr[CHIP_CONFIG_ERROR_STR_SIZE];
+static ErrorStrStorage sErrorStr;
 
 /**
  * Linked-list of error formatter functions.
@@ -38,7 +38,7 @@
 
 /**
  * This routine returns a human-readable NULL-terminated C string
- * describing the provided error.
+ * describing the provided error. This uses the global static storage.
  *
  * @param[in] err                      The error for format and describe.
  * @param[in] withSourceLocation       Whether or not to include the source
@@ -50,8 +50,26 @@
  */
 DLL_EXPORT const char * ErrorStr(CHIP_ERROR err, bool withSourceLocation)
 {
-    char * formattedError   = sErrorStr;
-    uint16_t formattedSpace = sizeof(sErrorStr);
+    return ErrorStr(err, withSourceLocation, sErrorStr);
+}
+
+/**
+ * This routine writess a human-readable NULL-terminated C string into the buf
+ * which describes the provided error.
+ *
+ * @param[in] err                      The error for format and describe.
+ * @param[in] withSourceLocation       Whether or not to include the source
+ * @param[in] storage                  ErrorStrStorage to write into
+ * location in the output string. Only used if CHIP_CONFIG_ERROR_SOURCE &&
+ * !CHIP_CONFIG_SHORT_ERROR_STR. Defaults to true.
+ *
+ * @return A pointer to a NULL-terminated C string describing the
+ *         provided error.
+ */
+DLL_EXPORT const char * ErrorStr(CHIP_ERROR err, bool withSourceLocation, ErrorStrStorage & storage)
+{
+    char * formattedError   = storage.buff;
+    uint16_t formattedSpace = storage.kBufferSize;
 
 #if CHIP_CONFIG_ERROR_SOURCE && !CHIP_CONFIG_SHORT_ERROR_STR
 
@@ -65,34 +83,27 @@
         formattedError += n;
         formattedSpace = static_cast<uint16_t>(formattedSpace - n);
     }
+#endif // CHIP_CONFIG_ERROR_SOURCE && !CHIP_CONFIG_SHORT_ERROR_STR
+
     if (err == CHIP_NO_ERROR)
     {
         (void) snprintf(formattedError, formattedSpace, CHIP_NO_ERROR_STRING);
-        return sErrorStr;
+        return storage.buff;
     }
 
-#else // CHIP_CONFIG_ERROR_SOURCE && !CHIP_CONFIG_SHORT_ERROR_STR
-
-    if (err == CHIP_NO_ERROR)
-    {
-        return CHIP_NO_ERROR_STRING;
-    }
-
-#endif // CHIP_CONFIG_ERROR_SOURCE && !CHIP_CONFIG_SHORT_ERROR_STR
-
     // Search the registered error formatter for one that will format the given
     // error code.
     for (const ErrorFormatter * errFormatter = sErrorFormatterList; errFormatter != nullptr; errFormatter = errFormatter->Next)
     {
         if (errFormatter->FormatError(formattedError, formattedSpace, err))
         {
-            return sErrorStr;
+            return storage.buff;
         }
     }
 
     // Use a default formatting if no formatter found.
     FormatError(formattedError, formattedSpace, nullptr, err, nullptr);
-    return sErrorStr;
+    return storage.buff;
 }
 
 /**
diff --git a/src/lib/core/ErrorStr.h b/src/lib/core/ErrorStr.h
index d67680c..f1365a6 100644
--- a/src/lib/core/ErrorStr.h
+++ b/src/lib/core/ErrorStr.h
@@ -48,7 +48,14 @@
     ErrorFormatter * Next;
 };
 
+struct ErrorStrStorage
+{
+    char buff[CHIP_CONFIG_ERROR_STR_SIZE];
+    constexpr static uint16_t kBufferSize = sizeof(buff);
+};
+
 extern const char * ErrorStr(CHIP_ERROR err, bool withSourceLocation = true);
+extern const char * ErrorStr(CHIP_ERROR err, bool withSourceLocation, ErrorStrStorage & storage);
 extern void RegisterErrorFormatter(ErrorFormatter * errFormatter);
 extern void DeregisterErrorFormatter(ErrorFormatter * errFormatter);
 extern void FormatError(char * buf, uint16_t bufSize, const char * subsys, CHIP_ERROR err, const char * desc);
diff --git a/src/lib/core/tests/TestCHIPErrorStr.cpp b/src/lib/core/tests/TestCHIPErrorStr.cpp
index 01130ac..1ce158c 100644
--- a/src/lib/core/tests/TestCHIPErrorStr.cpp
+++ b/src/lib/core/tests/TestCHIPErrorStr.cpp
@@ -170,6 +170,31 @@
 };
 // clang-format on
 
+void CheckCoreErrorStrHelper(const char * errStr, CHIP_ERROR err)
+{
+    char expectedText[9];
+
+    // Assert that the error string contains the error number in hex.
+    snprintf(expectedText, sizeof(expectedText), "%08" PRIX32, static_cast<uint32_t>(err.AsInteger()));
+    EXPECT_TRUE((strstr(errStr, expectedText) != nullptr));
+
+#if !CHIP_CONFIG_SHORT_ERROR_STR
+    // Assert that the error string contains a description, which is signaled
+    // by a presence of a colon proceeding the description.
+    EXPECT_TRUE((strchr(errStr, ':') != nullptr));
+#endif // !CHIP_CONFIG_SHORT_ERROR_STR
+
+#if CHIP_CONFIG_ERROR_SOURCE
+    // GetFile() should be relative to ${chip_root}
+    char const * const file = err.GetFile();
+    ASSERT_NE(file, nullptr);
+    EXPECT_EQ(strstr(file, "src/lib/core/"), file);
+
+    // File should be included in the error.
+    EXPECT_NE(strstr(errStr, file), nullptr);
+#endif // CHIP_CONFIG_ERROR_SOURCE
+}
+
 TEST(TestCHIPErrorStr, CheckCoreErrorStr)
 {
     // Register the layer error formatter
@@ -179,29 +204,46 @@
     // For each defined error...
     for (const auto & err : kTestElements)
     {
-        const char * errStr = ErrorStr(err);
-        char expectedText[9];
+        // ErrorStr with static char array.
+        CheckCoreErrorStrHelper(ErrorStr(err, /*withSourceLocation=*/true), err);
+    }
+}
 
-        // Assert that the error string contains the error number in hex.
-        snprintf(expectedText, sizeof(expectedText), "%08" PRIX32, static_cast<uint32_t>(err.AsInteger()));
-        EXPECT_TRUE((strstr(errStr, expectedText) != nullptr));
+TEST(TestCHIPErrorStr, CheckCoreErrorStrStorage)
+{
+    // Register the layer error formatter
+
+    RegisterCHIPLayerErrorFormatter();
+
+    // For each defined error...
+    for (const auto & err : kTestElements)
+    {
+        // ErrorStr with given storage.
+        ErrorStrStorage storage;
+        CheckCoreErrorStrHelper(ErrorStr(err, /*withSourceLocation=*/true, storage), err);
+    }
+}
+
+void CheckCoreErrorStrWithoutSourceLocationHelper(const char * errStr, CHIP_ERROR err)
+{
+    char expectedText[9];
+
+    // Assert that the error string contains the error number in hex.
+    snprintf(expectedText, sizeof(expectedText), "%08" PRIX32, static_cast<uint32_t>(err.AsInteger()));
+    EXPECT_TRUE((strstr(errStr, expectedText) != nullptr));
 
 #if !CHIP_CONFIG_SHORT_ERROR_STR
-        // Assert that the error string contains a description, which is signaled
-        // by a presence of a colon proceeding the description.
-        EXPECT_TRUE((strchr(errStr, ':') != nullptr));
+    // Assert that the error string contains a description, which is signaled
+    // by a presence of a colon proceeding the description.
+    EXPECT_TRUE((strchr(errStr, ':') != nullptr));
 #endif // !CHIP_CONFIG_SHORT_ERROR_STR
 
 #if CHIP_CONFIG_ERROR_SOURCE
-        // GetFile() should be relative to ${chip_root}
-        char const * const file = err.GetFile();
-        ASSERT_NE(file, nullptr);
-        EXPECT_EQ(strstr(file, "src/lib/core/"), file);
-
-        // File should be included in the error.
-        EXPECT_NE(strstr(errStr, file), nullptr);
+    char const * const file = err.GetFile();
+    ASSERT_NE(file, nullptr);
+    // File should not be included in the error.
+    EXPECT_EQ(strstr(errStr, file), nullptr);
 #endif // CHIP_CONFIG_ERROR_SOURCE
-    }
 }
 
 TEST(TestCHIPErrorStr, CheckCoreErrorStrWithoutSourceLocation)
@@ -213,24 +255,22 @@
     // For each defined error...
     for (const auto & err : kTestElements)
     {
-        const char * errStr = ErrorStr(err, /*withSourceLocation=*/false);
-        char expectedText[9];
+        // ErrorStr with static char array.
+        CheckCoreErrorStrWithoutSourceLocationHelper(ErrorStr(err, /*withSourceLocation=*/false), err);
+    }
+}
 
-        // Assert that the error string contains the error number in hex.
-        snprintf(expectedText, sizeof(expectedText), "%08" PRIX32, static_cast<uint32_t>(err.AsInteger()));
-        EXPECT_TRUE((strstr(errStr, expectedText) != nullptr));
+TEST(TestCHIPErrorStr, CheckCoreErrorStrStorageWithoutSourceLocation)
+{
+    // Register the layer error formatter
 
-#if !CHIP_CONFIG_SHORT_ERROR_STR
-        // Assert that the error string contains a description, which is signaled
-        // by a presence of a colon proceeding the description.
-        EXPECT_TRUE((strchr(errStr, ':') != nullptr));
-#endif // !CHIP_CONFIG_SHORT_ERROR_STR
+    RegisterCHIPLayerErrorFormatter();
 
-#if CHIP_CONFIG_ERROR_SOURCE
-        char const * const file = err.GetFile();
-        ASSERT_NE(file, nullptr);
-        // File should not be included in the error.
-        EXPECT_EQ(strstr(errStr, file), nullptr);
-#endif // CHIP_CONFIG_ERROR_SOURCE
+    // For each defined error...
+    for (const auto & err : kTestElements)
+    {
+        // ErrorStr with given storage.
+        ErrorStrStorage storage;
+        CheckCoreErrorStrWithoutSourceLocationHelper(ErrorStr(err, /*withSourceLocation=*/false, storage), err);
     }
 }
diff --git a/src/lib/support/tests/TestErrorStr.cpp b/src/lib/support/tests/TestErrorStr.cpp
index 809d69b..ac8a9f5 100644
--- a/src/lib/support/tests/TestErrorStr.cpp
+++ b/src/lib/support/tests/TestErrorStr.cpp
@@ -121,6 +121,13 @@
     EXPECT_STREQ(CHECK_AND_SKIP_SOURCE(ErrorStr(CHIP_NO_ERROR)), CHIP_NO_ERROR_STRING);
 }
 
+TEST(TestErrorStr, CheckErrorWithProvidedStorage)
+{
+    ErrorStrStorage storage;
+    EXPECT_STREQ(CHECK_AND_SKIP_SOURCE(ErrorStr(CHIP_NO_ERROR, true, storage)), CHIP_NO_ERROR_STRING);
+    EXPECT_STREQ(CHECK_AND_SKIP_SOURCE(ErrorStr(CHIP_ERROR_INTERNAL, true, storage)), "Error 0x000000AC");
+}
+
 TEST(TestErrorStr, CheckFormatErr)
 {
 #if CHIP_CONFIG_SHORT_ERROR_STR