This is an automated email from the ASF dual-hosted git repository.

paleolimbot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-nanoarrow.git


The following commit(s) were added to refs/heads/main by this push:
     new f341741d feat: Add LZ4 decompression support to IPC reader (#819)
f341741d is described below

commit f341741d26d810c1481e541600a64bb2e354b4eb
Author: Dewey Dunnington <[email protected]>
AuthorDate: Mon Oct 27 09:41:39 2025 -0500

    feat: Add LZ4 decompression support to IPC reader (#819)
    
    Closes #727.
    
    ---------
    
    Co-authored-by: Copilot <[email protected]>
---
 .github/workflows/build-and-test-ipc.yaml |  4 +-
 CMakeLists.txt                            | 34 +++++++++++++-
 ci/scripts/coverage.sh                    |  1 +
 src/nanoarrow/ipc/codecs.c                | 62 +++++++++++++++++++++++++
 src/nanoarrow/ipc/codecs_test.cc          | 75 ++++++++++++++++++++++++++++---
 src/nanoarrow/ipc/decoder_test.cc         | 43 +++++++++++++++---
 src/nanoarrow/ipc/files_test.cc           |  4 +-
 src/nanoarrow/nanoarrow_ipc.h             |  7 +++
 8 files changed, 214 insertions(+), 16 deletions(-)

diff --git a/.github/workflows/build-and-test-ipc.yaml 
b/.github/workflows/build-and-test-ipc.yaml
index d9a36561..e10dc3bf 100644
--- a/.github/workflows/build-and-test-ipc.yaml
+++ b/.github/workflows/build-and-test-ipc.yaml
@@ -43,7 +43,7 @@ jobs:
       fail-fast: false
       matrix:
         config:
-          - {label: default-build, cmake_args: "-DNANOARROW_BUILD_APPS=ON 
-DNANOARROW_IPC_WITH_ZSTD=ON"}
+          - {label: default-build, cmake_args: "-DNANOARROW_BUILD_APPS=ON 
-DNANOARROW_IPC_WITH_ZSTD=ON -DNANOARROW_IPC_WITH_LZ4=ON"}
           - {label: default-noatomics, cmake_args: 
"-DCMAKE_C_FLAGS='-DNANOARROW_IPC_USE_STDATOMIC=0'"}
           - {label: shared-test-linkage, cmake_args: 
"-DNANOARROW_TEST_LINKAGE_SHARED=ON"}
           - {label: namespaced-build, cmake_args: 
"-DNANOARROW_NAMESPACE=SomeUserNamespace"}
@@ -73,7 +73,7 @@ jobs:
         with:
           path: arrow
           # Bump the number at the end of this line to force a new Arrow C++ 
build
-          key: arrow-${{ runner.os }}-${{ runner.arch }}-4
+          key: arrow-${{ runner.os }}-${{ runner.arch }}-5
 
       - name: Build Arrow C++
         if: steps.cache-arrow-build.outputs.cache-hit != 'true'
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e9691778..0d70bf1e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,6 +40,7 @@ option(NANOARROW_FLATCC_INCLUDE_DIR "Include directory for 
flatcc includes" OFF)
 option(NANOARROW_FLATCC_LIB_DIR "Library directory that contains 
libflatccrt.a" OFF)
 option(NANOARROW_IPC_WITH_ZSTD "Build nanoarrow with ZSTD compression support 
built in"
        OFF)
+option(NANOARROW_IPC_WITH_LZ4 "Build nanoarrow with LZ4 compression support 
built in" OFF)
 
 option(NANOARROW_DEVICE "Build device extension" OFF)
 option(NANOARROW_TESTING "Build testing extension" OFF)
@@ -247,6 +248,32 @@ if(NANOARROW_IPC)
     endif()
   endif()
 
+  if(NANOARROW_IPC_WITH_LZ4)
+    if(NOT LZ4_LIBRARY_DIRS AND NOT LZ4_INCLUDE_DIRS)
+      find_package(PkgConfig REQUIRED)
+      pkg_check_modules(LZ4 REQUIRED liblz4)
+    endif()
+
+    if(NOT LZ4_LIBRARIES)
+      set(LZ4_LIBRARIES lz4)
+    endif()
+
+    message(STATUS "LZ4 library directories: ${LZ4_LIBRARY_DIRS}")
+    message(STATUS "LZ4 include directories: ${LZ4_INCLUDE_DIRS}")
+    message(STATUS "LZ4 libraries: ${LZ4_LIBRARIES}")
+
+    add_library(lz4::lz4 INTERFACE IMPORTED)
+    target_include_directories(lz4::lz4 INTERFACE ${LZ4_INCLUDE_DIRS})
+    target_link_libraries(lz4::lz4 INTERFACE ${LZ4_LIBRARIES})
+    if(LZ4_LIBRARY_DIRS)
+      set_target_properties(lz4::lz4 PROPERTIES INTERFACE_LINK_DIRECTORIES
+                                                "${LZ4_LIBRARY_DIRS}")
+    endif()
+
+    set(NANOARROW_IPC_EXTRA_FLAGS ${NANOARROW_IPC_EXTRA_FLAGS} 
"-DNANOARROW_IPC_WITH_LZ4")
+    set(NANOARROW_IPC_EXTRA_LIBS ${NANOARROW_IPC_EXTRA_LIBS} lz4::lz4)
+  endif()
+
   if(NOT NANOARROW_BUNDLE)
     set(NANOARROW_IPC_BUILD_SOURCES
         src/nanoarrow/ipc/codecs.c
@@ -296,8 +323,11 @@ if(NANOARROW_IPC AND (NANOARROW_BUILD_INTEGRATION_TESTS OR 
NANOARROW_BUILD_TESTS
                                     
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/src>
                                     $<INSTALL_INTERFACE:include>)
   target_link_libraries(nanoarrow_ipc_integration
-                        PRIVATE nanoarrow_testing_static nanoarrow_ipc_static 
flatccrt
-                                nanoarrow_coverage_config)
+                        PRIVATE nanoarrow_testing_static
+                                nanoarrow_ipc_static
+                                flatccrt
+                                nanoarrow_coverage_config
+                                ${NANOARROW_IPC_EXTRA_LIBS})
 endif()
 
 if(NANOARROW_DEVICE)
diff --git a/ci/scripts/coverage.sh b/ci/scripts/coverage.sh
index c22a7856..908d3dca 100755
--- a/ci/scripts/coverage.sh
+++ b/ci/scripts/coverage.sh
@@ -74,6 +74,7 @@ function main() {
 
     cmake "${TARGET_NANOARROW_DIR}" \
           -DNANOARROW_DEVICE=ON -DNANOARROW_IPC=ON 
-DNANOARROW_IPC_WITH_ZSTD=ON \
+          -DNANOARROW_IPC_WITH_LZ4=ON \
           -DNANOARROW_BUILD_TESTS=ON -DNANOARROW_BUILD_TESTS_WITH_ARROW=ON \
           -DNANOARROW_CODE_COVERAGE=ON
     cmake --build .
diff --git a/src/nanoarrow/ipc/codecs.c b/src/nanoarrow/ipc/codecs.c
index 552674ce..ab297989 100644
--- a/src/nanoarrow/ipc/codecs.c
+++ b/src/nanoarrow/ipc/codecs.c
@@ -54,6 +54,65 @@ ArrowIpcDecompressFunction 
ArrowIpcGetZstdDecompressionFunction(void) {
 #endif
 }
 
+#if defined(NANOARROW_IPC_WITH_LZ4)
+#include <lz4.h>
+#include <lz4frame.h>
+
+static ArrowErrorCode ArrowIpcDecompressLZ4(struct ArrowBufferView src, 
uint8_t* dst,
+                                            int64_t dst_size, struct 
ArrowError* error) {
+  LZ4F_errorCode_t ret;
+  LZ4F_decompressionContext_t ctx = NULL;
+
+  ret = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
+  if (LZ4F_isError(ret)) {
+    ArrowErrorSet(error, "LZ4 init failed");
+    return EIO;
+  }
+
+  size_t dst_capacity = dst_size;
+  size_t src_size = src.size_bytes;
+  ret = LZ4F_decompress(ctx, dst, &dst_capacity, src.data.data, &src_size,
+                        NULL /* options */);
+  if (LZ4F_isError(ret)) {
+    NANOARROW_UNUSED(LZ4F_freeDecompressionContext(ctx));
+    ArrowErrorSet(error,
+                  "LZ4F_decompress([buffer with %" PRId64
+                  " bytes] -> [buffer with %" PRId64 " bytes]) failed",
+                  src.size_bytes, dst_size);
+    return EIO;
+  }
+
+  if ((int64_t)dst_capacity != dst_size) {
+    NANOARROW_UNUSED(LZ4F_freeDecompressionContext(ctx));
+    ArrowErrorSet(error,
+                  "Expected decompressed size of %" PRId64 " bytes but got %" 
PRId64
+                  " bytes",
+                  dst_size, (int64_t)dst_capacity);
+    return EIO;
+  }
+
+  if (ret != 0) {
+    NANOARROW_UNUSED(LZ4F_freeDecompressionContext(ctx));
+    ArrowErrorSet(error,
+                  "Expected complete LZ4 frame but found frame with %" PRId64
+                  " bytes remaining",
+                  (int64_t)ret);
+    return EIO;
+  }
+
+  NANOARROW_UNUSED(LZ4F_freeDecompressionContext(ctx));
+  return NANOARROW_OK;
+}
+#endif
+
+ArrowIpcDecompressFunction ArrowIpcGetLZ4DecompressionFunction(void) {
+#if defined(NANOARROW_IPC_WITH_LZ4)
+  return &ArrowIpcDecompressLZ4;
+#else
+  return NULL;
+#endif
+}
+
 struct ArrowIpcSerialDecompressorPrivate {
   ArrowIpcDecompressFunction decompress_functions[3];
 };
@@ -115,6 +174,9 @@ ArrowErrorCode ArrowIpcSerialDecompressor(struct 
ArrowIpcDecompressor* decompres
   memset(decompressor->private_data, 0, sizeof(struct 
ArrowIpcSerialDecompressorPrivate));
   ArrowIpcSerialDecompressorSetFunction(decompressor, 
NANOARROW_IPC_COMPRESSION_TYPE_ZSTD,
                                         
ArrowIpcGetZstdDecompressionFunction());
+  ArrowIpcSerialDecompressorSetFunction(decompressor,
+                                        
NANOARROW_IPC_COMPRESSION_TYPE_LZ4_FRAME,
+                                        ArrowIpcGetLZ4DecompressionFunction());
   return NANOARROW_OK;
 }
 
diff --git a/src/nanoarrow/ipc/codecs_test.cc b/src/nanoarrow/ipc/codecs_test.cc
index 3c19f502..76278174 100644
--- a/src/nanoarrow/ipc/codecs_test.cc
+++ b/src/nanoarrow/ipc/codecs_test.cc
@@ -26,8 +26,8 @@
 const uint8_t kZstdCompressed012[] = {0x28, 0xb5, 0x2f, 0xfd, 0x20, 0x0c, 0x61,
                                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
                                       0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 
0x00};
-const uint8_t kZstdUncompressed012[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
-                                        0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
+const uint8_t kUncompressed012[] = {0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+                                    0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
 
 TEST(NanoarrowIpcTest, NanoarrowIpcZstdBuildMatchesRuntime) {
 #if defined(NANOARROW_IPC_WITH_ZSTD)
@@ -51,13 +51,13 @@ TEST(NanoarrowIpcTest, ZstdDecodeValidInput) {
   uint8_t out[16];
   std::memset(out, 0, sizeof(out));
   ASSERT_EQ(decompress({{&kZstdCompressed012}, sizeof(kZstdCompressed012)}, 
out,
-                       sizeof(kZstdUncompressed012), &error),
+                       sizeof(kUncompressed012), &error),
             NANOARROW_OK)
       << error.message;
-  EXPECT_TRUE(std::memcmp(out, kZstdUncompressed012, 
sizeof(kZstdUncompressed012)) == 0);
+  EXPECT_TRUE(std::memcmp(out, kUncompressed012, sizeof(kUncompressed012)) == 
0);
 
   ASSERT_EQ(decompress({{kZstdCompressed012}, sizeof(kZstdCompressed012)}, out,
-                       sizeof(kZstdUncompressed012) + 1, &error),
+                       sizeof(kUncompressed012) + 1, &error),
             EIO);
   EXPECT_STREQ(error.message, "Expected decompressed size of 13 bytes but got 
12 bytes");
 }
@@ -76,6 +76,71 @@ TEST(NanoarrowIpcTest, ZstdDecodeInvalidInput) {
                                     "with 0 bytes]) failed with error"));
 }
 
+// LZ4 compressed little endian int32s [0, 1, 2]
+const uint8_t kLZ4Compressed012[] = {
+    0x04, 0x22, 0x4d, 0x18, 0x60, 0x40, 0x82, 0x0c, 0x00, 0x00, 0x80, 0x00, 
0x00, 0x00,
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00};
+
+TEST(NanoarrowIpcTest, NanoarrowIpcLZ4BuildMatchesRuntime) {
+#if defined(NANOARROW_IPC_WITH_LZ4)
+  ASSERT_NE(ArrowIpcGetLZ4DecompressionFunction(), nullptr);
+#else
+  ASSERT_EQ(ArrowIpcGetLZ4DecompressionFunction(), nullptr);
+#endif
+}
+
+TEST(NanoarrowIpcTest, LZ4DecodeValidInput) {
+  auto decompress = ArrowIpcGetLZ4DecompressionFunction();
+  if (!decompress) {
+    GTEST_SKIP() << "nanoarrow_ipc not built with NANOARROW_IPC_WITH_LZ4";
+  }
+
+  struct ArrowError error {};
+
+  // Check a decompress of a valid compressed buffer
+  uint8_t out[16];
+  std::memset(out, 0, sizeof(out));
+  ASSERT_EQ(decompress({{&kLZ4Compressed012}, sizeof(kLZ4Compressed012)}, out,
+                       sizeof(kUncompressed012), &error),
+            NANOARROW_OK)
+      << error.message;
+  EXPECT_TRUE(std::memcmp(out, kUncompressed012, sizeof(kUncompressed012)) == 
0);
+
+  ASSERT_EQ(decompress({{kLZ4Compressed012}, sizeof(kLZ4Compressed012)}, out,
+                       sizeof(kUncompressed012) + 1, &error),
+            EIO);
+  EXPECT_STREQ(error.message, "Expected decompressed size of 13 bytes but got 
12 bytes");
+}
+
+TEST(NanoarrowIpcTest, LZ4DecodeInvalidInput) {
+  auto decompress = ArrowIpcGetLZ4DecompressionFunction();
+  if (!decompress) {
+    GTEST_SKIP() << "nanoarrow_ipc not built with NANOARROW_IPC_WITH_LZ4";
+  }
+
+  struct ArrowError error {};
+  uint8_t out[16];
+  std::memset(out, 0, sizeof(out));
+
+  // LZ4_decompress() needs almost correct data to trigger this failure branch
+  uint8_t src[16];
+  memcpy(src, kLZ4Compressed012, sizeof(src));
+  src[5] = 0xff;
+  ASSERT_EQ(decompress({{&src}, sizeof(src)}, out, sizeof(kUncompressed012), 
&error),
+            EIO);
+  EXPECT_THAT(
+      error.message,
+      ::testing::StartsWith(
+          "LZ4F_decompress([buffer with 16 bytes] -> [buffer with 12 bytes]) 
failed"));
+
+  // Nonsensical data triggers a different failure branch
+  const char* bad_data = "abcde";
+  EXPECT_EQ(decompress({{bad_data}, 5}, nullptr, 0, &error), EIO);
+  EXPECT_THAT(error.message,
+              ::testing::StartsWith(
+                  "Expected complete LZ4 frame but found frame with 6 bytes 
remaining"));
+}
+
 TEST(NanoarrowIpcTest, SerialDecompressor) {
   struct ArrowError error {};
   nanoarrow::ipc::UniqueDecompressor decompressor;
diff --git a/src/nanoarrow/ipc/decoder_test.cc 
b/src/nanoarrow/ipc/decoder_test.cc
index 058d9fb1..81993bce 100644
--- a/src/nanoarrow/ipc/decoder_test.cc
+++ b/src/nanoarrow/ipc/decoder_test.cc
@@ -108,7 +108,7 @@ alignas(8) static uint8_t kSimpleRecordBatch[] = {
     0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 
0x03, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-alignas(8) static uint8_t kSimpleRecordBatchCompressed[] = {
+alignas(8) static uint8_t kSimpleRecordBatchCompressedZstd[] = {
     0xff, 0xff, 0xff, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 
0x00, 0x00,
     0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x06, 0x00, 0x05, 0x00, 0x08, 0x00, 
0x0c, 0x00,
     0x0c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 
0x20, 0x00,
@@ -125,6 +125,23 @@ alignas(8) static uint8_t kSimpleRecordBatchCompressed[] = 
{
     0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 
0x00, 0x00,
     0x00, 0x00, 0x00, 0x00};
 
+alignas(8) static uint8_t kSimpleRecordBatchCompressedLZ4[] = {
+    0xff, 0xff, 0xff, 0xff, 0x98, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 
0x00, 0x00,
+    0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x06, 0x00, 0x05, 0x00, 0x08, 0x00, 
0x0c, 0x00,
+    0x0c, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 
0x28, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 
0x1c, 0x00,
+    0x10, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 
0x48, 0x00,
+    0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x03, 0x00, 
0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 
0x04, 0x00,
+    0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+    0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+    0x04, 0x22, 0x4d, 0x18, 0x60, 0x40, 0x82, 0x0c, 0x00, 0x00, 0x80, 0x00, 
0x00, 0x00,
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00};
+
 alignas(8) static uint8_t kSimpleRecordBatchUncompressible[] = {
     0xff, 0xff, 0xff, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 
0x00, 0x00,
     0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x06, 0x00, 0x05, 0x00, 0x08, 0x00, 
0x0c, 0x00,
@@ -399,15 +416,29 @@ TEST(NanoarrowIpcTest, 
NanoarrowIpcDecodeSimpleRecordBatch) {
       TestDecodeInt32Batch(kSimpleRecordBatch, sizeof(kSimpleRecordBatch), {1, 
2, 3}));
 }
 
-TEST(NanoarrowIpcTest, NanoarrowIpcDecodeCompressedRecordBatch) {
+TEST(NanoarrowIpcTest, NanoarrowIpcDecodeCompressedRecordBatchZstd) {
   if (ArrowIpcGetZstdDecompressionFunction() == nullptr) {
     EXPECT_FATAL_FAILURE(
-        TestDecodeInt32Batch(kSimpleRecordBatchCompressed,
-                             sizeof(kSimpleRecordBatchCompressed), {0, 1, 2}),
+        TestDecodeInt32Batch(kSimpleRecordBatchCompressedZstd,
+                             sizeof(kSimpleRecordBatchCompressedZstd), {0, 1, 
2}),
         "Compression type with value 2 not supported by this build of 
nanoarrow");
   } else {
-    ASSERT_NO_FATAL_FAILURE(TestDecodeInt32Batch(
-        kSimpleRecordBatchCompressed, sizeof(kSimpleRecordBatchCompressed), 
{0, 1, 2}));
+    
ASSERT_NO_FATAL_FAILURE(TestDecodeInt32Batch(kSimpleRecordBatchCompressedZstd,
+                                                 
sizeof(kSimpleRecordBatchCompressedZstd),
+                                                 {0, 1, 2}));
+  }
+}
+
+TEST(NanoarrowIpcTest, NanoarrowIpcDecodeCompressedRecordBatchLZ4) {
+  if (ArrowIpcGetLZ4DecompressionFunction() == nullptr) {
+    EXPECT_FATAL_FAILURE(
+        TestDecodeInt32Batch(kSimpleRecordBatchCompressedLZ4,
+                             sizeof(kSimpleRecordBatchCompressedLZ4), {0, 1, 
2}),
+        "Compression type with value 1 not supported by this build of 
nanoarrow");
+  } else {
+    
ASSERT_NO_FATAL_FAILURE(TestDecodeInt32Batch(kSimpleRecordBatchCompressedLZ4,
+                                                 
sizeof(kSimpleRecordBatchCompressedLZ4),
+                                                 {0, 1, 2}));
   }
 }
 
diff --git a/src/nanoarrow/ipc/files_test.cc b/src/nanoarrow/ipc/files_test.cc
index 55b988bb..ee2f384c 100644
--- a/src/nanoarrow/ipc/files_test.cc
+++ b/src/nanoarrow/ipc/files_test.cc
@@ -529,13 +529,15 @@ TEST_P(TestFileFixture, NanoarrowIpcTestFileIPCCheckJSON) 
{
 // At least one Windows MSVC version does not allow the #if defined()
 // to be within a macro invocation, so we define these two cases
 // with some repetition.
-#if defined(NANOARROW_IPC_WITH_ZSTD)
+#if defined(NANOARROW_IPC_WITH_ZSTD) && defined(NANOARROW_IPC_WITH_LZ4)
 INSTANTIATE_TEST_SUITE_P(
     NanoarrowIpcTest, TestFileFixture,
     ::testing::Values(
         // Testing of other files
         TestFile::OK("2.0.0-compression/generated_uncompressible_zstd.stream"),
         TestFile::OK("2.0.0-compression/generated_zstd.stream"),
+        TestFile::OK("2.0.0-compression/generated_uncompressible_lz4.stream"),
+        TestFile::OK("2.0.0-compression/generated_lz4.stream"),
         TestFile::OK("0.17.1/generated_union.stream"),
         TestFile::OK("0.14.1/generated_datetime.stream"),
         TestFile::OK("0.14.1/generated_decimal.stream"),
diff --git a/src/nanoarrow/nanoarrow_ipc.h b/src/nanoarrow/nanoarrow_ipc.h
index 5871d9bf..b9251a6b 100644
--- a/src/nanoarrow/nanoarrow_ipc.h
+++ b/src/nanoarrow/nanoarrow_ipc.h
@@ -31,6 +31,8 @@
   NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowIpcSharedBufferReset)
 #define ArrowIpcGetZstdDecompressionFunction \
   NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowIpcGetZstdDecompressionFunction)
+#define ArrowIpcGetLZ4DecompressionFunction \
+  NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowIpcGetLZ4DecompressionFunction)
 #define ArrowIpcSerialDecompressor \
   NANOARROW_SYMBOL(NANOARROW_NAMESPACE, ArrowIpcSerialDecompressor)
 #define ArrowIpcSerialDecompressorSetFunction \
@@ -253,6 +255,11 @@ typedef ArrowErrorCode 
(*ArrowIpcDecompressFunction)(struct ArrowBufferView src,
 /// The result will be NULL if nanoarrow was not built with 
NANOARROW_IPC_WITH_ZSTD.
 NANOARROW_DLL ArrowIpcDecompressFunction 
ArrowIpcGetZstdDecompressionFunction(void);
 
+/// \brief Get the decompression function for LZ4
+///
+/// The result will be NULL if nanoarrow was not built with 
NANOARROW_IPC_WITH_LZ4.
+NANOARROW_DLL ArrowIpcDecompressFunction 
ArrowIpcGetLZ4DecompressionFunction(void);
+
 /// \brief An ArrowIpcDecompressor implementation that performs decompression 
in serial
 NANOARROW_DLL ArrowErrorCode
 ArrowIpcSerialDecompressor(struct ArrowIpcDecompressor* decompressor);

Reply via email to