Commit 3288f8f3 for libheif

commit 3288f8f3b5bdd7fd976a96c3666470fb53f314a4
Author: Dirk Farin <dirk.farin@gmail.com>
Date:   Thu Aug 27 00:41:44 2026 +0200

    tests: move the box-building helpers into test_utils

    Six regression tests carried identical copies of put_u32_be, put_u16_be,
    append_fourcc, append, append_cstr and make_box for assembling synthetic
    HEIF files. The GCC 13 workaround in the previous commit had to be applied
    six times, which is the point at which the duplication stops being cheap.

    Declare them in test_utils.h and define them once in test_utils.cc, which
    every test already links through the testframework library. The two
    make_box variants produced the same bytes; the shared one uses the
    single-allocation version from decompression_bomb.cc.

diff --git a/tests/decompression_bomb.cc b/tests/decompression_bomb.cc
index db1c157a..3b1e4669 100644
--- a/tests/decompression_bomb.cc
+++ b/tests/decompression_bomb.cc
@@ -37,6 +37,7 @@

 #include "catch_amalgamated.hpp"
 #include "libheif/heif.h"
+#include "test_utils.h"
 #include "compression.h"

 #include <cstdint>
@@ -45,58 +46,6 @@

 namespace {

-void put_u32_be(std::vector<uint8_t>& out, uint32_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 24));
-  out.push_back(static_cast<uint8_t>(v >> 16));
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void put_u16_be(std::vector<uint8_t>& out, uint16_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void append_fourcc(std::vector<uint8_t>& out, const char fourcc[4]) {
-  // Append the bytes one at a time. A range insert from the char array makes
-  // GCC 13 report a bogus -Wstringop-overflow in optimized builds.
-  for (int i = 0; i < 4; i++) {
-    out.push_back(static_cast<uint8_t>(fourcc[i]));
-  }
-}
-
-// Append a null-terminated string (as read by Box_infe via read_string()).
-void append_cstr(std::vector<uint8_t>& out, const char* s) {
-  while (*s) { out.push_back(static_cast<uint8_t>(*s)); ++s; }
-  out.push_back(0);
-}
-
-void append(std::vector<uint8_t>& out, const std::vector<uint8_t>& v) {
-  out.insert(out.end(), v.begin(), v.end());
-}
-
-std::vector<uint8_t> make_box(const char fourcc[4],
-                              const std::vector<uint8_t>& payload,
-                              bool is_full_box = false,
-                              uint8_t version = 0,
-                              uint32_t flags = 0) {
-  const size_t header_size = is_full_box ? 12 : 8;
-
-  std::vector<uint8_t> box;
-  box.reserve(header_size + payload.size());
-
-  put_u32_be(box, static_cast<uint32_t>(header_size + payload.size()));
-  append_fourcc(box, fourcc);
-  if (is_full_box) {
-    box.push_back(version);
-    box.push_back(static_cast<uint8_t>(flags >> 16));
-    box.push_back(static_cast<uint8_t>(flags >> 8));
-    box.push_back(static_cast<uint8_t>(flags));
-  }
-  append(box, payload);
-  return box;
-}
-
 // Build a minimal HEIF file with two items:
 //   item 1 ('mski', 8x8, 8bpp): the primary image. The mask codec needs no
 //           bitstream (its "compressed" data is just the raw pixel bytes), so no
diff --git a/tests/entity_groups.cc b/tests/entity_groups.cc
index 1e745a83..8aebefdc 100644
--- a/tests/entity_groups.cc
+++ b/tests/entity_groups.cc
@@ -26,6 +26,7 @@

 #include "catch_amalgamated.hpp"
 #include "libheif/heif.h"
+#include "test_utils.h"
 #include "libheif/heif_entity_groups.h"

 #include <cstdint>
@@ -34,47 +35,6 @@

 namespace {

-void put_u32_be(std::vector<uint8_t>& out, uint32_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 24));
-  out.push_back(static_cast<uint8_t>(v >> 16));
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void put_u16_be(std::vector<uint8_t>& out, uint16_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void append_fourcc(std::vector<uint8_t>& out, const char fourcc[4]) {
-  // Append the bytes one at a time. A range insert from the char array makes
-  // GCC 13 report a bogus -Wstringop-overflow in optimized builds.
-  for (int i = 0; i < 4; i++) {
-    out.push_back(static_cast<uint8_t>(fourcc[i]));
-  }
-}
-
-std::vector<uint8_t> make_box(const char fourcc[4],
-                              const std::vector<uint8_t>& payload,
-                              bool is_full_box = false,
-                              uint8_t version = 0,
-                              uint32_t flags = 0) {
-  std::vector<uint8_t> body;
-  if (is_full_box) {
-    body.push_back(version);
-    body.push_back(static_cast<uint8_t>(flags >> 16));
-    body.push_back(static_cast<uint8_t>(flags >> 8));
-    body.push_back(static_cast<uint8_t>(flags));
-  }
-  body.insert(body.end(), payload.begin(), payload.end());
-
-  std::vector<uint8_t> box;
-  put_u32_be(box, static_cast<uint32_t>(8 + body.size()));
-  append_fourcc(box, fourcc);
-  box.insert(box.end(), body.begin(), body.end());
-  return box;
-}
-
 // Build a minimal HEIF file whose 'grpl' box contains a single child box
 // whose four-cc is NOT one of the registered Box_EntityToGroup subclasses
 // (pymd / altr / ster). Such children parse to Box_other and used to
diff --git a/tests/grid_tile_missing.cc b/tests/grid_tile_missing.cc
index de55dcaf..5901cb12 100644
--- a/tests/grid_tile_missing.cc
+++ b/tests/grid_tile_missing.cc
@@ -26,6 +26,7 @@

 #include "catch_amalgamated.hpp"
 #include "libheif/heif.h"
+#include "test_utils.h"
 #include "libheif/heif_tiling.h"

 #include <cstdint>
@@ -33,51 +34,6 @@

 namespace {

-void put_u32_be(std::vector<uint8_t>& out, uint32_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 24));
-  out.push_back(static_cast<uint8_t>(v >> 16));
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void put_u16_be(std::vector<uint8_t>& out, uint16_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void append_fourcc(std::vector<uint8_t>& out, const char fourcc[4]) {
-  // Append the bytes one at a time. A range insert from the char array makes
-  // GCC 13 report a bogus -Wstringop-overflow in optimized builds.
-  for (int i = 0; i < 4; i++) {
-    out.push_back(static_cast<uint8_t>(fourcc[i]));
-  }
-}
-
-void append(std::vector<uint8_t>& out, const std::vector<uint8_t>& v) {
-  out.insert(out.end(), v.begin(), v.end());
-}
-
-std::vector<uint8_t> make_box(const char fourcc[4],
-                              const std::vector<uint8_t>& payload,
-                              bool is_full_box = false,
-                              uint8_t version = 0,
-                              uint32_t flags = 0) {
-  std::vector<uint8_t> body;
-  if (is_full_box) {
-    body.push_back(version);
-    body.push_back(static_cast<uint8_t>(flags >> 16));
-    body.push_back(static_cast<uint8_t>(flags >> 8));
-    body.push_back(static_cast<uint8_t>(flags));
-  }
-  body.insert(body.end(), payload.begin(), payload.end());
-
-  std::vector<uint8_t> box;
-  put_u32_be(box, static_cast<uint32_t>(8 + body.size()));
-  append_fourcc(box, fourcc);
-  box.insert(box.end(), body.begin(), body.end());
-  return box;
-}
-
 // Build a minimal HEIF with one 'grid' item (id=1, 1x1, 64x64) whose 'dimg'
 // iref references a tile item id (99) that has no corresponding 'infe' entry.
 // Used to crash ImageItem_Grid::decode_grid_tile() with a NULL deref because
diff --git a/tests/iden_declared_size.cc b/tests/iden_declared_size.cc
index 368f3e47..f22249aa 100644
--- a/tests/iden_declared_size.cc
+++ b/tests/iden_declared_size.cc
@@ -26,57 +26,13 @@

 #include "catch_amalgamated.hpp"
 #include "libheif/heif.h"
+#include "test_utils.h"

 #include <cstdint>
 #include <vector>

 namespace {

-void put_u32_be(std::vector<uint8_t>& out, uint32_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 24));
-  out.push_back(static_cast<uint8_t>(v >> 16));
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void put_u16_be(std::vector<uint8_t>& out, uint16_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void append_fourcc(std::vector<uint8_t>& out, const char fourcc[4]) {
-  // Append the bytes one at a time. A range insert from the char array makes
-  // GCC 13 report a bogus -Wstringop-overflow in optimized builds.
-  for (int i = 0; i < 4; i++) {
-    out.push_back(static_cast<uint8_t>(fourcc[i]));
-  }
-}
-
-void append(std::vector<uint8_t>& out, const std::vector<uint8_t>& v) {
-  out.insert(out.end(), v.begin(), v.end());
-}
-
-std::vector<uint8_t> make_box(const char fourcc[4],
-                              const std::vector<uint8_t>& payload,
-                              bool is_full_box = false,
-                              uint8_t version = 0,
-                              uint32_t flags = 0) {
-  std::vector<uint8_t> body;
-  if (is_full_box) {
-    body.push_back(version);
-    body.push_back(static_cast<uint8_t>(flags >> 16));
-    body.push_back(static_cast<uint8_t>(flags >> 8));
-    body.push_back(static_cast<uint8_t>(flags));
-  }
-  body.insert(body.end(), payload.begin(), payload.end());
-
-  std::vector<uint8_t> box;
-  put_u32_be(box, static_cast<uint32_t>(8 + body.size()));
-  append_fourcc(box, fourcc);
-  box.insert(box.end(), body.begin(), body.end());
-  return box;
-}
-
 // Build a minimal HEIF file with two items:
 //   item 1 ('mski', 8x8, 8bpp): real, decodable content (the mask codec needs
 //           no bitstream -- its "compressed" data is just the raw pixel bytes).
diff --git a/tests/overlay_amplification.cc b/tests/overlay_amplification.cc
index 833808f6..52af7187 100644
--- a/tests/overlay_amplification.cc
+++ b/tests/overlay_amplification.cc
@@ -36,6 +36,7 @@

 #include "catch_amalgamated.hpp"
 #include "libheif/heif.h"
+#include "test_utils.h"

 #include <cstdint>
 #include <string>
@@ -43,51 +44,6 @@

 namespace {

-void put_u32_be(std::vector<uint8_t>& out, uint32_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 24));
-  out.push_back(static_cast<uint8_t>(v >> 16));
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void put_u16_be(std::vector<uint8_t>& out, uint16_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void append_fourcc(std::vector<uint8_t>& out, const char fourcc[4]) {
-  // Append the bytes one at a time. A range insert from the char array makes
-  // GCC 13 report a bogus -Wstringop-overflow in optimized builds.
-  for (int i = 0; i < 4; i++) {
-    out.push_back(static_cast<uint8_t>(fourcc[i]));
-  }
-}
-
-void append(std::vector<uint8_t>& out, const std::vector<uint8_t>& v) {
-  out.insert(out.end(), v.begin(), v.end());
-}
-
-std::vector<uint8_t> make_box(const char fourcc[4],
-                              const std::vector<uint8_t>& payload,
-                              bool is_full_box = false,
-                              uint8_t version = 0,
-                              uint32_t flags = 0) {
-  std::vector<uint8_t> body;
-  if (is_full_box) {
-    body.push_back(version);
-    body.push_back(static_cast<uint8_t>(flags >> 16));
-    body.push_back(static_cast<uint8_t>(flags >> 8));
-    body.push_back(static_cast<uint8_t>(flags));
-  }
-  body.insert(body.end(), payload.begin(), payload.end());
-
-  std::vector<uint8_t> box;
-  put_u32_be(box, static_cast<uint32_t>(8 + body.size()));
-  append_fourcc(box, fourcc);
-  box.insert(box.end(), body.begin(), body.end());
-  return box;
-}
-
 // ImageOverlay payload (ISO/IEC 23008-12): version, flags, 4x background color,
 // canvas width/height, and one (x,y) offset per composited image. flags=0 uses
 // 16-bit fields. All composited images are placed at (0,0) here.
diff --git a/tests/test_utils.cc b/tests/test_utils.cc
index 7ab38824..95319a20 100644
--- a/tests/test_utils.cc
+++ b/tests/test_utils.cc
@@ -219,3 +219,64 @@ std::string get_tests_output_file_path(const char* filename)
   dir /= filename;
   return dir.string();
 }
+
+
+void put_u32_be(std::vector<uint8_t>& out, uint32_t v)
+{
+  out.push_back(static_cast<uint8_t>(v >> 24));
+  out.push_back(static_cast<uint8_t>(v >> 16));
+  out.push_back(static_cast<uint8_t>(v >> 8));
+  out.push_back(static_cast<uint8_t>(v));
+}
+
+void put_u16_be(std::vector<uint8_t>& out, uint16_t v)
+{
+  out.push_back(static_cast<uint8_t>(v >> 8));
+  out.push_back(static_cast<uint8_t>(v));
+}
+
+void append_fourcc(std::vector<uint8_t>& out, const char fourcc[4])
+{
+  // Append the bytes one at a time. A range insert from the char array makes
+  // GCC 13 report a bogus -Wstringop-overflow in optimized builds.
+  for (int i = 0; i < 4; i++) {
+    out.push_back(static_cast<uint8_t>(fourcc[i]));
+  }
+}
+
+void append_cstr(std::vector<uint8_t>& out, const char* s)
+{
+  while (*s) {
+    out.push_back(static_cast<uint8_t>(*s));
+    ++s;
+  }
+  out.push_back(0);
+}
+
+void append(std::vector<uint8_t>& out, const std::vector<uint8_t>& v)
+{
+  out.insert(out.end(), v.begin(), v.end());
+}
+
+std::vector<uint8_t> make_box(const char fourcc[4],
+                              const std::vector<uint8_t>& payload,
+                              bool is_full_box,
+                              uint8_t version,
+                              uint32_t flags)
+{
+  const size_t header_size = is_full_box ? 12 : 8;
+
+  std::vector<uint8_t> box;
+  box.reserve(header_size + payload.size());
+
+  put_u32_be(box, static_cast<uint32_t>(header_size + payload.size()));
+  append_fourcc(box, fourcc);
+  if (is_full_box) {
+    box.push_back(version);
+    box.push_back(static_cast<uint8_t>(flags >> 16));
+    box.push_back(static_cast<uint8_t>(flags >> 8));
+    box.push_back(static_cast<uint8_t>(flags));
+  }
+  append(box, payload);
+  return box;
+}
diff --git a/tests/test_utils.h b/tests/test_utils.h
index 97ec114a..dbb6d1d7 100644
--- a/tests/test_utils.h
+++ b/tests/test_utils.h
@@ -24,7 +24,9 @@
   SOFTWARE.
 */

+#include <cstdint>
 #include <string>
+#include <vector>
 #include "libheif/heif.h"

 #include <filesystem>
@@ -51,3 +53,26 @@ heif_encoder* get_encoder_or_skip_test(heif_compression_format format);
 fs::path get_tests_output_dir();

 std::string get_tests_output_file_path(const char* filename);
+
+
+// --- Helpers to build ISOBMFF byte streams for synthetic test files ---
+
+// Append a big-endian integer.
+void put_u32_be(std::vector<uint8_t>& out, uint32_t v);
+void put_u16_be(std::vector<uint8_t>& out, uint16_t v);
+
+// Append the four characters of a box type or brand.
+void append_fourcc(std::vector<uint8_t>& out, const char fourcc[4]);
+
+// Append a null-terminated string, including the terminator.
+void append_cstr(std::vector<uint8_t>& out, const char* s);
+
+// Append the contents of another byte vector.
+void append(std::vector<uint8_t>& out, const std::vector<uint8_t>& v);
+
+// Build a box, or a full box with version and flags, from its type and payload.
+std::vector<uint8_t> make_box(const char fourcc[4],
+                              const std::vector<uint8_t>& payload,
+                              bool is_full_box = false,
+                              uint8_t version = 0,
+                              uint32_t flags = 0);
diff --git a/tests/uncompressed_tile_range_overflow.cc b/tests/uncompressed_tile_range_overflow.cc
index 5042374a..56aa6afe 100644
--- a/tests/uncompressed_tile_range_overflow.cc
+++ b/tests/uncompressed_tile_range_overflow.cc
@@ -51,63 +51,13 @@

 #include "catch_amalgamated.hpp"
 #include "libheif/heif.h"
+#include "test_utils.h"

 #include <cstdint>
 #include <vector>

 namespace {

-void put_u32_be(std::vector<uint8_t>& out, uint32_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 24));
-  out.push_back(static_cast<uint8_t>(v >> 16));
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void put_u16_be(std::vector<uint8_t>& out, uint16_t v) {
-  out.push_back(static_cast<uint8_t>(v >> 8));
-  out.push_back(static_cast<uint8_t>(v));
-}
-
-void append_fourcc(std::vector<uint8_t>& out, const char fourcc[4]) {
-  // Append the bytes one at a time. A range insert from the char array makes
-  // GCC 13 report a bogus -Wstringop-overflow in optimized builds.
-  for (int i = 0; i < 4; i++) {
-    out.push_back(static_cast<uint8_t>(fourcc[i]));
-  }
-}
-
-void append_cstr(std::vector<uint8_t>& out, const char* s) {
-  while (*s) { out.push_back(static_cast<uint8_t>(*s)); ++s; }
-  out.push_back(0);
-}
-
-void append(std::vector<uint8_t>& out, const std::vector<uint8_t>& v) {
-  out.insert(out.end(), v.begin(), v.end());
-}
-
-std::vector<uint8_t> make_box(const char fourcc[4],
-                              const std::vector<uint8_t>& payload,
-                              bool is_full_box = false,
-                              uint8_t version = 0,
-                              uint32_t flags = 0) {
-  const size_t header_size = is_full_box ? 12 : 8;
-
-  std::vector<uint8_t> box;
-  box.reserve(header_size + payload.size());
-
-  put_u32_be(box, static_cast<uint32_t>(header_size + payload.size()));
-  append_fourcc(box, fourcc);
-  if (is_full_box) {
-    box.push_back(version);
-    box.push_back(static_cast<uint8_t>(flags >> 16));
-    box.push_back(static_cast<uint8_t>(flags >> 8));
-    box.push_back(static_cast<uint8_t>(flags));
-  }
-  append(box, payload);
-  return box;
-}
-
 constexpr uint32_t WIDTH = 4096;
 constexpr uint32_t HEIGHT = 4096;
 constexpr uint32_t TILE_COLS = 4096;