Module: Zstandard::API

Defined in:
lib/zstandard/api.rb

Overview

Internal API layer to abstract different libzstd calling semantics/versions

Class Method Summary collapse

Class Method Details

.decompressed_size(string) ⇒ Integer

Tries to gather the size of the decompressed data.

Parameters:

  • string (String)

    Compressed data

Returns:

  • (Integer)

    size of the decompressed data or 0


56
57
58
59
60
61
62
63
64
65
66
67
68
# 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 = {}) ⇒ Object


70
71
72
73
74
75
76
77
78
79
80
81
82
83
# 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 = {}) ⇒ Object


85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 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 = {}) ⇒ Object


7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 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) ⇒ Object


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# 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