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

xndai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/orc.git


The following commit(s) were added to refs/heads/master by this push:
     new 599a784  ORC-395: Support ZSTD in C++ writer/reader
599a784 is described below

commit 599a78402b8854e09a785260e23d439290a183e3
Author: Gang Wu <[email protected]>
AuthorDate: Tue Nov 6 09:10:10 2018 -0800

    ORC-395: Support ZSTD in C++ writer/reader
    
    * ORC-395: Support ZSTD in C++ writer/reader
    
    * add MSVC support for zstd build
    
    * fix MSVC build
    
    * refactor ensureHeader function
    
    * add output check
---
 c++/src/CMakeLists.txt                  |   2 +
 c++/src/Compression.cc                  | 207 ++++++++++++++++++++++++++++++--
 c++/test/TestCompression.cc             |  30 ++++-
 cmake_modules/FindZSTD.cmake            |  68 +++++++++++
 cmake_modules/ThirdpartyToolchain.cmake |  54 +++++++++
 5 files changed, 345 insertions(+), 16 deletions(-)

diff --git a/c++/src/CMakeLists.txt b/c++/src/CMakeLists.txt
index 7358361..72d408d 100644
--- a/c++/src/CMakeLists.txt
+++ b/c++/src/CMakeLists.txt
@@ -169,6 +169,7 @@ include_directories (
   ${SNAPPY_INCLUDE_DIR}
   ${LZ4_INCLUDE_DIR}
   ${LIBHDFSPP_INCLUDE_DIR}
+  ${ZSTD_INCLUDE_DIR}
   )
 
 add_custom_command(OUTPUT orc_proto.pb.h orc_proto.pb.cc
@@ -220,6 +221,7 @@ target_link_libraries (orc
   zlib
   snappy
   lz4
+  zstd
   ${LIBHDFSPP_LIBRARIES}
   )
 
diff --git a/c++/src/Compression.cc b/c++/src/Compression.cc
index 6bd0220..d1f7c4f 100644
--- a/c++/src/Compression.cc
+++ b/c++/src/Compression.cc
@@ -28,6 +28,7 @@
 #include <sstream>
 
 #include "zlib.h"
+#include "zstd.h"
 
 #include "wrap/snappy-wrapper.h"
 
@@ -57,6 +58,9 @@ namespace orc {
       buffer[2] = static_cast<char>(compressedSize >> 15);
     }
 
+    // ensure enough room for compression block header
+    void ensureHeader();
+
     // Buffer to hold uncompressed data until user calls Next()
     DataBuffer<unsigned char> rawInputBuffer;
 
@@ -117,6 +121,22 @@ namespace orc {
            static_cast<uint64_t>(outputSize - outputPosition);
   }
 
+  void CompressionStreamBase::ensureHeader() {
+    // adjust 3 bytes for the compression header
+    if (outputPosition + 3 >= outputSize) {
+      int newPosition = outputPosition + 3 - outputSize;
+      if (!BufferedOutputStream::Next(
+        reinterpret_cast<void **>(&outputBuffer),
+        &outputSize)) {
+        throw std::runtime_error(
+          "Failed to get next output buffer from output stream.");
+      }
+      outputPosition = newPosition;
+    } else {
+      outputPosition += 3;
+    }
+  }
+
   /**
    * Streaming compression base class
    */
@@ -151,19 +171,7 @@ namespace orc {
 
   bool CompressionStream::Next(void** data, int*size) {
     if (bufferSize != 0) {
-      // adjust 3 bytes for the compression header
-      if (outputPosition + 3 >= outputSize) {
-        int newPosition = outputPosition + 3 - outputSize;
-        if (!BufferedOutputStream::Next(
-          reinterpret_cast<void **>(&outputBuffer),
-          &outputSize)) {
-          throw std::runtime_error(
-            "Failed to get next output buffer from output stream.");
-        }
-        outputPosition = newPosition;
-      } else {
-        outputPosition += 3;
-      }
+      ensureHeader();
 
       uint64_t totalCompressedSize = doStreamingCompression();
 
@@ -899,6 +907,169 @@ DIAGNOSTIC_POP
     return static_cast<uint64_t>(result);
   }
 
+  /**
+   * Block compression base class
+   */
+  class BlockCompressionStream: public CompressionStreamBase {
+  public:
+    BlockCompressionStream(OutputStream * outStream,
+                           int compressionLevel,
+                           uint64_t capacity,
+                           uint64_t blockSize,
+                           MemoryPool& pool)
+                           : CompressionStreamBase(outStream,
+                                                   compressionLevel,
+                                                   capacity,
+                                                   blockSize,
+                                                   pool)
+                           , compressorBuffer(pool) {
+      // PASS
+    }
+
+    virtual bool Next(void** data, int*size) override;
+    virtual std::string getName() const override = 0;
+
+  protected:
+    // compresses a block and returns the compressed size
+    virtual uint64_t doBlockCompression() = 0;
+
+    // return maximum possible compression size for allocating space for
+    // compressorBuffer below
+    virtual uint64_t estimateMaxCompressionSize() = 0;
+
+    // should allocate max possible compressed size
+    DataBuffer<unsigned char> compressorBuffer;
+  };
+
+  bool BlockCompressionStream::Next(void** data, int*size) {
+    if (bufferSize != 0) {
+      ensureHeader();
+
+      // perform compression
+      size_t totalCompressedSize = doBlockCompression();
+
+      const unsigned char * dataToWrite = nullptr;
+      int totalSizeToWrite = 0;
+      char * header = outputBuffer + outputPosition - 3;
+
+      if (totalCompressedSize >= static_cast<size_t>(bufferSize)) {
+        writeHeader(header, static_cast<size_t>(bufferSize), true);
+        dataToWrite = rawInputBuffer.data();
+        totalSizeToWrite = bufferSize;
+      } else {
+        writeHeader(header, totalCompressedSize, false);
+        dataToWrite = compressorBuffer.data();
+        totalSizeToWrite = static_cast<int>(totalCompressedSize);
+      }
+
+      char * dst = header + 3;
+      while (totalSizeToWrite > 0) {
+        if (outputPosition == outputSize) {
+          if (!BufferedOutputStream::Next(reinterpret_cast<void 
**>(&outputBuffer),
+                                          &outputSize)) {
+            throw std::logic_error(
+              "Failed to get next output buffer from output stream.");
+          }
+          outputPosition = 0;
+          dst = outputBuffer;
+        } else if (outputPosition > outputSize) {
+          // this will unlikely happen, but we have seen a few on zstd v1.1.0
+          throw std::logic_error("Write to an out-of-bound place!");
+        }
+
+        int sizeToWrite = std::min(totalSizeToWrite, outputSize - 
outputPosition);
+        std::memcpy(dst, dataToWrite, static_cast<size_t>(sizeToWrite));
+
+        outputPosition += sizeToWrite;
+        dataToWrite += sizeToWrite;
+        totalSizeToWrite -= sizeToWrite;
+        dst += sizeToWrite;
+      }
+    }
+
+    *data = rawInputBuffer.data();
+    *size = static_cast<int>(rawInputBuffer.size());
+    bufferSize = *size;
+    compressorBuffer.resize(estimateMaxCompressionSize());
+
+    return true;
+  }
+
+  /**
+   * ZSTD block compression
+   */
+  class ZSTDCompressionStream: public BlockCompressionStream {
+  public:
+    ZSTDCompressionStream(OutputStream * outStream,
+                          int compressionLevel,
+                          uint64_t capacity,
+                          uint64_t blockSize,
+                          MemoryPool& pool)
+                          : BlockCompressionStream(outStream,
+                                                   compressionLevel,
+                                                   capacity,
+                                                   blockSize,
+                                                   pool) {
+      // PASS
+    }
+
+    virtual std::string getName() const override {
+      return "ZstdCompressionStream";
+    }
+
+  protected:
+    virtual uint64_t doBlockCompression() override;
+
+    virtual uint64_t estimateMaxCompressionSize() override {
+      return ZSTD_compressBound(static_cast<size_t>(bufferSize));
+    }
+  };
+
+  uint64_t ZSTDCompressionStream::doBlockCompression() {
+    return ZSTD_compress(compressorBuffer.data(),
+                         compressorBuffer.size(),
+                         rawInputBuffer.data(),
+                         static_cast<size_t>(bufferSize),
+                         level);
+  }
+
+  /**
+   * ZSTD block decompression
+   */
+  class ZSTDDecompressionStream: public BlockDecompressionStream {
+  public:
+    ZSTDDecompressionStream(std::unique_ptr<SeekableInputStream> inStream,
+                            size_t blockSize,
+                            MemoryPool& pool)
+                            : BlockDecompressionStream(std::move(inStream),
+                                                       blockSize,
+                                                       pool) {
+      // PASS
+    }
+
+    std::string getName() const override {
+      std::ostringstream result;
+      result << "zstd(" << getStreamName() << ")";
+      return result.str();
+    }
+
+  protected:
+    virtual uint64_t decompress(const char *input,
+                                uint64_t length,
+                                char *output,
+                                size_t maxOutputLength) override;
+  };
+
+  uint64_t ZSTDDecompressionStream::decompress(const char *input,
+                                               uint64_t length,
+                                               char *output,
+                                               size_t maxOutputLength) {
+    return static_cast<uint64_t>(ZSTD_decompress(output,
+                                                 maxOutputLength,
+                                                 input,
+                                                 length));
+  }
+
   std::unique_ptr<BufferedOutputStream>
      createCompressor(
                       CompressionKind kind,
@@ -920,6 +1091,13 @@ DIAGNOSTIC_POP
         (new ZlibCompressionStream(
                 outStream, level, bufferCapacity, compressionBlockSize, pool));
     }
+    case CompressionKind_ZSTD: {
+      int level = (strategy == CompressionStrategy_SPEED) ?
+              1 : ZSTD_CLEVEL_DEFAULT;
+      return std::unique_ptr<BufferedOutputStream>
+        (new ZSTDCompressionStream(
+          outStream, level, bufferCapacity, compressionBlockSize, pool));
+    }
     case CompressionKind_SNAPPY:
     case CompressionKind_LZO:
     case CompressionKind_LZ4:
@@ -948,6 +1126,9 @@ DIAGNOSTIC_POP
     case CompressionKind_LZ4:
       return std::unique_ptr<SeekableInputStream>
         (new Lz4DecompressionStream(std::move(input), blockSize, pool));
+    case CompressionKind_ZSTD:
+      return std::unique_ptr<SeekableInputStream>
+        (new ZSTDDecompressionStream(std::move(input), blockSize, pool));
     default: {
       std::ostringstream buffer;
       buffer << "Unknown compression codec " << kind;
diff --git a/c++/test/TestCompression.cc b/c++/test/TestCompression.cc
index dcc25e8..9ca2160 100644
--- a/c++/test/TestCompression.cc
+++ b/c++/test/TestCompression.cc
@@ -137,7 +137,7 @@ namespace orc {
     compress_original_string(CompressionKind_ZLIB);
   }
 
-  void zlib_compress_simple_repeated_string(orc::CompressionKind kind) {
+  void compress_simple_repeated_string(orc::CompressionKind kind) {
     MemoryOutputStream memStream(DEFAULT_MEM_STREAM_SIZE);
     MemoryPool * pool = getDefaultPool();
 
@@ -161,8 +161,8 @@ namespace orc {
                         *pool);
   }
 
-  TEST(TestCompression, zlib_compress_simple_repeated_string) {
-    zlib_compress_simple_repeated_string(CompressionKind_ZLIB);
+  TEST(TestCompression, compress_simple_repeated_string) {
+    compress_simple_repeated_string(CompressionKind_ZLIB);
   }
 
   void compress_two_blocks(orc::CompressionKind kind) {
@@ -312,4 +312,28 @@ namespace orc {
   TEST(TestCompression, zlib_protobuff_compression) {
     protobuff_compression(CompressionKind_ZLIB, proto::ZLIB);
   }
+
+  TEST(Compression, zstd_compress_original_string) {
+    compress_original_string(CompressionKind_ZSTD);
+  }
+
+  TEST(Compression, zstd_compress_simple_repeated_string) {
+    compress_simple_repeated_string(CompressionKind_ZSTD);
+  }
+
+  TEST(Compression, zstd_compress_two_blocks) {
+    compress_two_blocks(CompressionKind_ZSTD);
+  }
+
+  TEST(Compression, zstd_compress_random_letters) {
+    compress_random_letters(CompressionKind_ZSTD);
+  }
+
+  TEST(Compression, zstd_compress_random_bytes) {
+    compress_random_bytes(CompressionKind_ZSTD);
+  }
+
+  TEST(Compression, zstd_protobuff_compression) {
+    protobuff_compression(CompressionKind_ZSTD, proto::ZSTD);
+  }
 }
diff --git a/cmake_modules/FindZSTD.cmake b/cmake_modules/FindZSTD.cmake
new file mode 100644
index 0000000..3dba072
--- /dev/null
+++ b/cmake_modules/FindZSTD.cmake
@@ -0,0 +1,68 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# ZSTD_HOME environmental variable is used to check for Zstd headers and 
static library
+
+# ZSTD_INCLUDE_DIR: directory containing headers
+# ZSTD_LIBS: directory containing zstd libraries
+# ZSTD_STATIC_LIB: path to libzstd.a
+# ZSTD_FOUND: whether zstd has been found
+
+if( NOT "${ZSTD_HOME}" STREQUAL "")
+  file (TO_CMAKE_PATH "${ZSTD_HOME}" _zstd_path)
+endif()
+
+message (STATUS "ZSTD_HOME: ${ZSTD_HOME}")
+
+find_path (ZSTD_INCLUDE_DIR zstd.h HINTS
+        ${_zstd_path}
+        NO_DEFAULT_PATH
+        PATH_SUFFIXES "include")
+
+find_library (ZSTD_LIBRARIES NAMES zstd HINTS
+        ${_zstd_path}
+        PATH_SUFFIXES "lib")
+
+if (ZSTD_INCLUDE_DIR AND ZSTD_LIBRARIES)
+  set (ZSTD_FOUND TRUE)
+  get_filename_component (ZSTD_LIBS ${ZSTD_LIBRARIES} PATH)
+  set (ZSTD_HEADER_NAME zstd.h)
+  set (ZSTD_HEADER ${ZSTD_INCLUDE_DIR}/${ZSTD_HEADER_NAME})
+  set (ZSTD_LIB_NAME zstd)
+  set (ZSTD_STATIC_LIB 
${ZSTD_LIBS}/${CMAKE_STATIC_LIBRARY_PREFIX}${ZSTD_LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX})
+else ()
+  set (ZSTD_FOUND FALSE)
+endif ()
+
+if (ZSTD_FOUND)
+  message (STATUS "Found the zstd header: ${ZSTD_HEADER}")
+  message (STATUS "Found the zstd library: ${ZSTD_STATIC_LIB}")
+else()
+  if (_ZSTD_path)
+    set (ZSTD_ERR_MSG "Could not find zstd. Looked in ${_zstd_path}.")
+  else ()
+    set (ZSTD_ERR_MSG "Could not find zstd in system search paths.")
+  endif()
+
+  if (ZSTD_FIND_REQUIRED)
+    message (FATAL_ERROR "${ZSTD_ERR_MSG}")
+  else ()
+    message (STATUS "${ZSTD_ERR_MSG}")
+  endif ()
+endif()
+
+mark_as_advanced (
+        ZSTD_INCLUDE_DIR
+        ZSTD_STATIC_LIB
+        ZSTD_LIBS
+        ZSTD_LIBRARIES
+)
diff --git a/cmake_modules/ThirdpartyToolchain.cmake 
b/cmake_modules/ThirdpartyToolchain.cmake
index 8aabf69..9bf97de 100644
--- a/cmake_modules/ThirdpartyToolchain.cmake
+++ b/cmake_modules/ThirdpartyToolchain.cmake
@@ -15,6 +15,7 @@ set(SNAPPY_VERSION "1.1.7")
 set(ZLIB_VERSION "1.2.11")
 set(GTEST_VERSION "1.8.0")
 set(PROTOBUF_VERSION "3.5.1")
+set(ZSTD_VERSION "1.3.5")
 
 set(THIRDPARTY_DIR "${CMAKE_BINARY_DIR}/c++/libs/thirdparty")
 set(THIRDPARTY_LOG_OPTIONS LOG_CONFIGURE 1
@@ -44,6 +45,10 @@ if (DEFINED ENV{PROTOBUF_HOME})
   set (PROTOBUF_HOME "$ENV{PROTOBUF_HOME}")
 endif ()
 
+if (DEFINED ENV{ZSTD_HOME})
+  set (ZSTD_HOME "$ENV{ZSTD_HOME}")
+endif ()
+
 if (DEFINED ENV{GTEST_HOME})
   set (GTEST_HOME "$ENV{GTEST_HOME}")
 endif ()
@@ -125,6 +130,55 @@ if (ZLIB_VENDORED)
 endif ()
 
 # ----------------------------------------------------------------------
+# Zstd
+
+if (NOT "${ZSTD_HOME}" STREQUAL "")
+  find_package (zstd REQUIRED)
+  set(ZSTD_VENDORED FALSE)
+else ()
+  set(ZSTD_HOME "${THIRDPARTY_DIR}/zstd_ep-install")
+  set(ZSTD_INCLUDE_DIR "${ZSTD_HOME}/include")
+  if (MSVC)
+    set(ZSTD_STATIC_LIB_NAME zstd_static)
+    if (${UPPERCASE_BUILD_TYPE} STREQUAL "DEBUG")
+      set(ZSTD_STATIC_LIB_NAME ${ZSTD_STATIC_LIB_NAME})
+    endif ()
+  else ()
+    set(ZSTD_STATIC_LIB_NAME zstd)
+  endif ()
+  set(ZSTD_STATIC_LIB 
"${ZSTD_HOME}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${ZSTD_STATIC_LIB_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX}")
+  set(ZSTD_CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${ZSTD_HOME}
+          -DBUILD_SHARED_LIBS=OFF)
+
+  if (CMAKE_VERSION VERSION_GREATER "3.7")
+    set(ZSTD_CONFIGURE SOURCE_SUBDIR "build/cmake" CMAKE_ARGS 
${ZSTD_CMAKE_ARGS})
+  else()
+    set(ZSTD_CONFIGURE CONFIGURE_COMMAND "${THIRDPARTY_CONFIGURE_COMMAND}" 
${ZSTD_CMAKE_ARGS}
+            
"${CMAKE_CURRENT_BINARY_DIR}/zstd_ep-prefix/src/zstd_ep/build/cmake")
+  endif()
+
+  ExternalProject_Add(zstd_ep
+          URL 
"https://github.com/facebook/zstd/archive/v${ZSTD_VERSION}.tar.gz";
+          ${ZSTD_CONFIGURE}
+          ${THIRDPARTY_LOG_OPTIONS}
+          BUILD_BYPRODUCTS ${ZSTD_STATIC_LIB})
+
+  set(ZSTD_VENDORED TRUE)
+endif ()
+
+include_directories (SYSTEM ${ZSTD_INCLUDE_DIR})
+add_library (zstd STATIC IMPORTED)
+set_target_properties (zstd PROPERTIES IMPORTED_LOCATION ${ZSTD_STATIC_LIB})
+
+if (ZSTD_VENDORED)
+  add_dependencies (zstd zstd_ep)
+  if (INSTALL_VENDORED_LIBS)
+    install(FILES "${ZSTD_STATIC_LIB}"
+            DESTINATION "lib")
+  endif ()
+endif ()
+
+# ----------------------------------------------------------------------
 # LZ4
 
 if (NOT "${LZ4_HOME}" STREQUAL "")

Reply via email to