SDL: Add SDLTest_CompareMemory function to compare memory ranges

From 07b7ec68eb168206891c0730bc676895ddc646b6 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Sat, 3 Feb 2024 04:45:48 +0100
Subject: [PATCH] Add SDLTest_CompareMemory function to compare memory ranges

---
 include/SDL3/SDL_test_compare.h | 13 +++++++
 src/test/SDL_test_compare.c     | 68 +++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)

diff --git a/include/SDL3/SDL_test_compare.h b/include/SDL3/SDL_test_compare.h
index 82de5d2494c8f..2aa84e00ae1ae 100644
--- a/include/SDL3/SDL_test_compare.h
+++ b/include/SDL3/SDL_test_compare.h
@@ -55,6 +55,19 @@ extern "C" {
  */
 int SDLTest_CompareSurfaces(SDL_Surface *surface, SDL_Surface *referenceSurface, int allowable_error);
 
+/**
+ * Compares 2 memory blocks for equality
+ *
+ * \param actual Memory used in comparison, displayed on the left
+ * \param size_actual Size of actual in bytes
+ * \param reference Reference memory, displayed on the right
+ * \param size_reference Size of reference in bytes
+ *
+ * \returns 0 if the left and right memory block are equal, non-zero if they are non-equal.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern int SDLTest_CompareMemory(const void *actual, size_t size_actual, const void *reference, size_t size_reference);
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
diff --git a/src/test/SDL_test_compare.c b/src/test/SDL_test_compare.c
index 0cfda10e027e9..db2fd04772072 100644
--- a/src/test/SDL_test_compare.c
+++ b/src/test/SDL_test_compare.c
@@ -149,3 +149,71 @@ int SDLTest_CompareSurfaces(SDL_Surface *surface, SDL_Surface *referenceSurface,
 
     return ret;
 }
+
+int SDLTest_CompareMemory(const void *actual, size_t size_actual, const void *reference, size_t size_reference) {
+    const size_t size_max = SDL_max(size_actual, size_reference);
+    size_t i;
+    struct {
+        const Uint8 *data;
+        size_t size;
+    } columns[2] = {
+        {
+            actual,
+            size_actual,
+        },
+        {
+            reference,
+            size_reference,
+        },
+    };
+
+#define WIDTH 16
+
+    SDLTest_AssertCheck(size_actual == size_reference, "Sizes of memory blocks must be equal (actual=%" SDL_PRIu64 " expected=%" SDL_PRIu64 ")", (Uint64)size_actual, (Uint64)size_reference);
+    if (size_actual == size_reference) {
+        int equals;
+        equals = SDL_memcmp(actual, reference, size_max) == 0;
+        SDLTest_AssertCheck(equals, "Memory blocks contain the same data (actual | reference)");
+        if (equals) {
+            return 0;
+        }
+    }
+
+    for (i = 0; i < size_max; i += WIDTH) {
+        char line_buffer[16 + SDL_arraysize(columns) * (4 * WIDTH + 1) + (SDL_arraysize(columns) - 1) * 2 + 1];
+        size_t pos = 0;
+        size_t col;
+
+        pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, "%016" SDL_PRIx64 , (Uint64)i);
+
+        for (col = 0; col < SDL_arraysize(columns); col++) {
+            size_t j;
+
+            for (j = 0; j < WIDTH; j++) {
+                if (i + j < columns[col].size) {
+                    pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, " %02x", columns[col].data[i + j]);
+                } else {
+                    pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, "   ");
+                }
+            }
+            pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, " ");
+            for (j = 0; j < WIDTH; j++) {
+                char c = ' ';
+                if (i + j < columns[col].size) {
+                    c = columns[col].data[i + j];
+                    if (!SDL_isprint(c)) {
+                        c = '.';
+                    }
+                }
+                pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer) - pos, "%c", c);
+            }
+            if (col < SDL_arraysize(columns) - 1) {
+                pos += SDL_snprintf(line_buffer + pos, SDL_arraysize(line_buffer), " |");
+            }
+        }
+        SDLTest_LogError("%s", line_buffer);
+        SDL_assert(pos == SDL_arraysize(line_buffer) - 1);
+    }
+#undef WIDTH
+    return 1;
+}