module Zstandard::API

Internal API layer to abstract different libzstd calling semantics/versions

Public Class Methods

decompressed_size(string) click to toggle source

Tries to gather the size of the decompressed data.

@param [String] string Compressed data @return [Integer] size of the decompressed data or 0

# File lib/zstandard/api.rb, line 56
def self.decompressed_size(string)
  if FFIBindings.zstd_version_number < 600
    parameters = FFIBindings::ZSTD_parameters.new
    FFIBindings.zstd_get_frame_params(parameters, string, string.bytesize)
    parameters[:srcSize]
  elsif FFIBindings.zstd_version_number < 10104
    frame_params = FFIBindings::ZSTD_frameParams.new
    FFIBindings.zstd_get_frame_params(frame_params, string, string.bytesize)
    frame_params[:frameContentSize]
  else
    FFIBindings.zstd_find_decompressed_size(string, string.bytesize)
  end
end
simple_compress(string, options = {}) click to toggle source
# File lib/zstandard/api.rb, line 70
def self.simple_compress(string, options = {})
  level = options[:level] || 0 # 0 means default

  dst_size = FFIBindings.zstd_compress_bound(string.bytesize)
  dst = FFI::MemoryPointer.new(:char, dst_size)

  error_code = number_of_bytes = FFIBindings.zstd_compress(dst, dst_size, string, string.bytesize, level)

  if FFIBindings.zstd_is_error(error_code) >= 0
    dst.read_bytes(number_of_bytes)
  else
    raise "error"
  end
end
simple_decompress(string, options = {}) click to toggle source
# File lib/zstandard/api.rb, line 85
def self.simple_decompress(string, options = {})
  #
  # The docs state, that one should be carefull when using the simple decompress API, because
  # it relies on the upfront knowledge of the decompressed (dst) size. This information may
  # by present within the frame header (or not). If it's present, it can be very large and/or
  # intentionally modified, so it's vital to check that this value is within the systems limits
  # and fallback to streaming decompression if unsure.
  #
  dst = FFI::MemoryPointer.new(:char, dst_size = API.decompressed_size(string))
  error_code = number_of_bytes = FFIBindings.zstd_decompress(dst, dst_size, string, string.bytesize)

  if FFIBindings.zstd_is_error(error_code) != 0
    raise FFIBindings.zstd_get_error_name(error_code).read_string
  else
    dst.read_bytes(number_of_bytes)
  end
end
streaming_decompress(string, options = {}) click to toggle source
# File lib/zstandard/api.rb, line 7
def self.streaming_decompress(string, options = {})
  dst_size = window_size(string)

  # The docs propose to check the dst size (windowSize), because it could be manipulated
  raise "Invalid dst size!" if dst_size <= 0 || dst_size > Config::MAX_STREAMING_DECOMRPESS_BUFFER_SIZE

  src = FFI::MemoryPointer.from_string(string) # we need the pointer for arithmetics
  dst = FFI::MemoryPointer.new(:char, dst_size)

  dst_offset = 0
  src_offset = 0
  result = []

  dctx = FFIBindings.zstd_create_dctx
  FFIBindings.zstd_decompress_begin(dctx)

  while (src_size = FFIBindings.zstd_next_src_size_to_deompress(dctx)) != 0
    nbytes = FFIBindings.zstd_decompress_continue(
      dctx,
      dst + dst_offset,
      (dst + dst_offset).size,
      src + src_offset,
      src_size
    )

    if FFIBindings.zstd_is_error(error_code = nbytes) > 0
      raise FFIBindings.zstd_get_error_name(error_code)
    elsif nbytes > 0
      result << (dst + dst_offset).read_bytes(nbytes)
      dst_offset += nbytes
      dst_offset = 0 if (dst + dst_offset).size == 0
    end

    src_offset += src_size
  end

  dst.free
  src.free
  FFIBindings.zstd_free_dctx(dctx)

  result.join
end
window_size(string) click to toggle source
# File lib/zstandard/api.rb, line 103
def self.window_size(string)
  if FFIBindings.zstd_version_number < 600
    parameters = FFIBindings::ZSTD_parameters.new
    FFIBindings.zstd_get_frame_params(parameters, string, string.bytesize)
    2 ** parameters[:windowLog]
  elsif FFIBindings.zstd_version_number < 700
    frame_params = FFIBindings::ZSTD_frameParams.new
    FFIBindings.zstd_get_frame_params(frame_params, string, string.bytesize)
    2 ** frame_params[:windowLog]
  elsif Zstandard::FFIBindings.zstd_version_number < 10300
    frame_params = FFIBindings::ZSTD_frameParams.new
    FFIBindings.zstd_get_frame_params(frame_params, string, string.bytesize)
    frame_params[:windowSize]
  else
    frame_header = FFIBindings::ZSTD_frameHeader.new
    FFIBindings.zstd_get_frame_header(frame_header, string, string.bytesize)
    frame_header[:windowSize]
  end
end