zstandard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +16 -0
- data/.yardopts +1 -0
- data/Gemfile +31 -0
- data/LICENSE.txt +21 -0
- data/README.md +179 -0
- data/Rakefile +13 -0
- data/lib/zstandard.rb +23 -0
- data/lib/zstandard/api.rb +123 -0
- data/lib/zstandard/config.rb +24 -0
- data/lib/zstandard/ffi_bindings.rb +214 -0
- data/lib/zstandard/version.rb +3 -0
- data/run_rspec_with_bundled_libraries.sh +20 -0
- data/zstandard.gemspec +24 -0
- metadata +73 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: be082988dab6b74aeeb9b9a22f5d3bc2a7c4e458
|
4
|
+
data.tar.gz: b85823b5c30374c5bbef7b5d3ff34db8d0bfad1b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 72bcd58d297a430824218ec3dacb656f11aaa118e53cc952aa1e575202e6e24fa253d1d831eec16733d0ced2521809fb39d7da76b83e1cc52728b1f85a7b0b11
|
7
|
+
data.tar.gz: 248e3858167a8e58e7baaf494803e54ff3a19f2e83a440db1b3027328546b82589eecd993b2717035ed3e64a682d62d3139cf0dc53f8862711e18067c668f4ce
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 1.9.3
|
5
|
+
- 2.0.0
|
6
|
+
- 2.1.10
|
7
|
+
- 2.2.6
|
8
|
+
- 2.3.3
|
9
|
+
- 2.4.1
|
10
|
+
- jruby-1.7.26
|
11
|
+
- jruby-9.1.7.0
|
12
|
+
|
13
|
+
before_install:
|
14
|
+
- gem install bundler -v 1.13.6
|
15
|
+
|
16
|
+
script: bundle && ./run_rspec_with_bundled_libraries.sh
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup=markdown
|
data/Gemfile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in zstandard.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
gem "benchmark-ips"
|
8
|
+
gem "bundler"
|
9
|
+
gem "rake"
|
10
|
+
gem "ruby-progressbar", "~> 1.0"
|
11
|
+
gem "rspec", "~> 3.0"
|
12
|
+
|
13
|
+
if RUBY_ENGINE == "ruby"
|
14
|
+
gem "simplecov" if RUBY_VERSION >= "2.0.0"
|
15
|
+
|
16
|
+
if !ENV["CI"]
|
17
|
+
gem "pry"
|
18
|
+
|
19
|
+
if RUBY_VERSION < "2.0.0"
|
20
|
+
gem "pry-nav"
|
21
|
+
else
|
22
|
+
gem "pry-byebug"
|
23
|
+
end
|
24
|
+
|
25
|
+
# yard and friends
|
26
|
+
gem "redcarpet"
|
27
|
+
gem "github-markup"
|
28
|
+
gem "yard"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Michael Sievers
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
# zstandard-ruby
|
2
|
+
|
3
|
+
[](https://travis-ci.org/msievers/zstandard-ruby)
|
4
|
+
|
5
|
+
* [Installation](#installation)
|
6
|
+
* [Usage](#usage)
|
7
|
+
* [Advanced Usage](#advanced-usage)
|
8
|
+
* [Configuration](#configuration)
|
9
|
+
* [Docs](#docs)
|
10
|
+
* [Examples for installing `libzstd`](#examples-for-installing-libzstd)
|
11
|
+
* [Contributing](#contributing)
|
12
|
+
* [License](#license)
|
13
|
+
|
14
|
+
This gem implements FFI based bindings to the [Zstandard](https://facebook.github.io/zstd) compression library `libzstd`. The [Zstandard](https://facebook.github.io/zstd) compression algorithm shines because it compresses data with the same or better ratio as *Zlib* but does this (much) faster, depending on the input. For the majority of cases it's **faster and better** then *Zlib*.
|
15
|
+
|
16
|
+
It **does not** ship the actual `libzstd` library but expects some version to be present on your system.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
Make sure you have `libzstd` installed on your system. In doubt, have a look at the [examples for installing `libzstd`](#examples-for-installing-libzstd).
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem "zstandard"
|
26
|
+
```
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
```
|
31
|
+
bundle
|
32
|
+
```
|
33
|
+
|
34
|
+
Or install it yourself as:
|
35
|
+
|
36
|
+
```
|
37
|
+
gem install zstandard
|
38
|
+
```
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
The gem provides an API which aims compatibility the with `zlib` gem. There are two module methods
|
43
|
+
* `deflate(string, level)`
|
44
|
+
* `inflate(compressed_string)`
|
45
|
+
|
46
|
+
The only difference between this and the `zlib` gem is the interpretation of the compression level. For `zlib`, this is a value between `1..9`, whereas for `Zstandard` it's between `1..22`.
|
47
|
+
|
48
|
+
For most use cases, you should try to keep the compression level (very) low for `Zstandard`, because often compression time increases without significant better compression ratios. If in doubt, *do not specify* a compression level at all, which will use the default compression level.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
require "zstandard"
|
52
|
+
|
53
|
+
compressed_string = Zstandard.deflate(string)
|
54
|
+
decompressed_string = Zstandard.inflate(compressed_string)
|
55
|
+
```
|
56
|
+
|
57
|
+
## Advanced Usage
|
58
|
+
|
59
|
+
*This is not intended to be used by regular users.*
|
60
|
+
|
61
|
+
Besides the high level API which targets compatibility with the well known `zlib` gem there are two additional layers you can interact with. There is a low-level API which tries to cover differences between various `libzstd` version, e.g. different *frame header* formats. You should only use this if you know, what you are doing.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
require "zstandard"
|
65
|
+
|
66
|
+
compressed_string = Zstandard::API.simple_compress(string)
|
67
|
+
decompressed_string = Zstandard::API.simple_decompress(compressed_string)
|
68
|
+
```
|
69
|
+
|
70
|
+
The most low-level bindings are exposed via `Zstandard::FFIBindings`. If there is any reason for this, you can do the following.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
require "zstandard"
|
74
|
+
|
75
|
+
zstd_library_version = Zstandard::FFIBindings.zstd_version_number
|
76
|
+
```
|
77
|
+
|
78
|
+
## Configuration
|
79
|
+
|
80
|
+
This gem can be configured by setting various environment variables. Please be carefull if you decide to change/overwrite any of these. The default values are carefully choosen and there should be no need to alter one of these for regular use cases.
|
81
|
+
|
82
|
+
### `ZSTANDARD_LIBRARY`
|
83
|
+
|
84
|
+
If you have `libzstd` installed in some unusual location or if you want to explictly tell, which library to use, you can set `ZSTANDARD_LIBRARY` to the path of the library you want to use. This can be handy for example if you have the latest version compiled in `/usr/local/lib`, but your system has an old version in `/usr/lib`.
|
85
|
+
|
86
|
+
```
|
87
|
+
ZSTANDARD_LIBRARY=/usr/local/lib/libzstd.so bundle exec rake rspec
|
88
|
+
```
|
89
|
+
|
90
|
+
### `ZSTANDARD_MAX_SIMPLE_DECOMPRESS_SIZE`
|
91
|
+
|
92
|
+
This specifies the maximum (decompressed) size of a string for which the simple decompression approach should be used. In order minimise memory consumption, if the expected decompressed size exceeds this limit, streaming decompression is used.
|
93
|
+
|
94
|
+
### `ZSTANDARD_MAX_STREAMING_DECOMRPESS_BUFFER_SIZE`
|
95
|
+
|
96
|
+
For streaming decompression, this specifies the size of the decompression bufffer.
|
97
|
+
|
98
|
+
## Docs
|
99
|
+
|
100
|
+
Yard generated docs can be found at [http://www.rubydoc.info/github/msievers/zstandard-ruby](http://www.rubydoc.info/github/msievers/zstandard-ruby).
|
101
|
+
|
102
|
+
## Examples for installing `libzstd`
|
103
|
+
|
104
|
+
* [Debian](#debian)
|
105
|
+
* [Fedora](#fedora)
|
106
|
+
* [FreeBSD](#freebsd)
|
107
|
+
* [Mac](#mac)
|
108
|
+
* [NetBSD](#netbsd)
|
109
|
+
* [Ubuntu](#ubuntu)
|
110
|
+
|
111
|
+
### Debian
|
112
|
+
|
113
|
+
#### Jessie (8.x)
|
114
|
+
|
115
|
+
The package is only included in `sid`, the unstable Debian version. There are guides describing how to install unstable packages into a stable Debian, for example at [Linuxaria](https://linuxaria.com/howto/how-to-install-a-single-package-from-debian-sid-or-debian-testing) or [serverfault.com](https://serverfault.com/questions/22414/how-can-i-run-debian-stable-but-install-some-packages-from-testing).
|
116
|
+
|
117
|
+
```
|
118
|
+
# run as root
|
119
|
+
|
120
|
+
apt-get install zstd
|
121
|
+
```
|
122
|
+
|
123
|
+
### Fedora
|
124
|
+
|
125
|
+
#### Fedora 23
|
126
|
+
|
127
|
+
```
|
128
|
+
sudo dnf install libzstd
|
129
|
+
```
|
130
|
+
|
131
|
+
### FreeBSD
|
132
|
+
|
133
|
+
#### FreeBSD 10
|
134
|
+
|
135
|
+
```
|
136
|
+
# run as root
|
137
|
+
|
138
|
+
portsnap fetch && portsnap extract
|
139
|
+
|
140
|
+
cd /usr/ports/archivers/zstd
|
141
|
+
make install
|
142
|
+
```
|
143
|
+
|
144
|
+
### Mac
|
145
|
+
|
146
|
+
```
|
147
|
+
brew install zstd
|
148
|
+
```
|
149
|
+
|
150
|
+
### NetBSD
|
151
|
+
|
152
|
+
#### NetBSD 7
|
153
|
+
|
154
|
+
```
|
155
|
+
# run as root
|
156
|
+
|
157
|
+
# the following assumes you are running a x86_64 system with NetBSD 7.0.x
|
158
|
+
|
159
|
+
export PATH="/usr/pkg/sbin:$PATH"
|
160
|
+
export PKG_PATH="ftp://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/x86_64/7.0_current/All/"
|
161
|
+
|
162
|
+
pkg_add zstd
|
163
|
+
```
|
164
|
+
|
165
|
+
### Ubuntu
|
166
|
+
|
167
|
+
#### Xenial Xerus (16.04) and above
|
168
|
+
|
169
|
+
```
|
170
|
+
sudo apt-get install zstd
|
171
|
+
```
|
172
|
+
|
173
|
+
## Contributing
|
174
|
+
|
175
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/msievers/zstandard-ruby](https://github.com/msievers/zstandard-ruby).
|
176
|
+
|
177
|
+
## License
|
178
|
+
|
179
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
Dir.glob("benchmarks/*.rake").each do |file|
|
9
|
+
import file
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Run benchmarks"
|
13
|
+
task :benchmark => ["benchmarks:deflate"]
|
data/lib/zstandard.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "./zstandard/api"
|
2
|
+
require_relative "./zstandard/config"
|
3
|
+
require_relative "./zstandard/version"
|
4
|
+
|
5
|
+
module Zstandard
|
6
|
+
class Error < ::StandardError; end;
|
7
|
+
class DecompressedSizeUnknownError < Error; end;
|
8
|
+
class LibraryVersionNotSupportedError < Error; end;
|
9
|
+
|
10
|
+
def self.deflate(string, level = nil)
|
11
|
+
API.simple_compress(string, level: level)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.inflate(string)
|
15
|
+
decompressed_size = API.decompressed_size(string)
|
16
|
+
|
17
|
+
if decompressed_size > 0 && decompressed_size <= Config::MAX_SIMPLE_DECOMPRESS_SIZE
|
18
|
+
API.simple_decompress(string)
|
19
|
+
else
|
20
|
+
API.streaming_decompress(string)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require_relative "./config"
|
2
|
+
require_relative "./ffi_bindings"
|
3
|
+
|
4
|
+
module Zstandard
|
5
|
+
# Internal API layer to abstract different libzstd calling semantics/versions
|
6
|
+
module API
|
7
|
+
def self.streaming_decompress(string, options = {})
|
8
|
+
dst_size = window_size(string)
|
9
|
+
|
10
|
+
# The docs propose to check the dst size (windowSize), because it could be manipulated
|
11
|
+
raise "Invalid dst size!" if dst_size <= 0 || dst_size > Config::MAX_STREAMING_DECOMRPESS_BUFFER_SIZE
|
12
|
+
|
13
|
+
src = FFI::MemoryPointer.from_string(string) # we need the pointer for arithmetics
|
14
|
+
dst = FFI::MemoryPointer.new(:char, dst_size)
|
15
|
+
|
16
|
+
dst_offset = 0
|
17
|
+
src_offset = 0
|
18
|
+
result = []
|
19
|
+
|
20
|
+
dctx = FFIBindings.zstd_create_dctx
|
21
|
+
FFIBindings.zstd_decompress_begin(dctx)
|
22
|
+
|
23
|
+
while (src_size = FFIBindings.zstd_next_src_size_to_deompress(dctx)) != 0
|
24
|
+
nbytes = FFIBindings.zstd_decompress_continue(
|
25
|
+
dctx,
|
26
|
+
dst + dst_offset,
|
27
|
+
(dst + dst_offset).size,
|
28
|
+
src + src_offset,
|
29
|
+
src_size
|
30
|
+
)
|
31
|
+
|
32
|
+
if FFIBindings.zstd_is_error(error_code = nbytes) > 0
|
33
|
+
raise FFIBindings.zstd_get_error_name(error_code)
|
34
|
+
elsif nbytes > 0
|
35
|
+
result << (dst + dst_offset).read_bytes(nbytes)
|
36
|
+
dst_offset += nbytes
|
37
|
+
dst_offset = 0 if (dst + dst_offset).size == 0
|
38
|
+
end
|
39
|
+
|
40
|
+
src_offset += src_size
|
41
|
+
end
|
42
|
+
|
43
|
+
dst.free
|
44
|
+
src.free
|
45
|
+
FFIBindings.zstd_free_dctx(dctx)
|
46
|
+
|
47
|
+
result.join
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Tries to gather the size of the decompressed data.
|
52
|
+
#
|
53
|
+
# @param [String] string Compressed data
|
54
|
+
# @return [Integer] size of the decompressed data or 0
|
55
|
+
#
|
56
|
+
def self.decompressed_size(string)
|
57
|
+
if FFIBindings.zstd_version_number < 600
|
58
|
+
parameters = FFIBindings::ZSTD_parameters.new
|
59
|
+
FFIBindings.zstd_get_frame_params(parameters, string, string.bytesize)
|
60
|
+
parameters[:srcSize]
|
61
|
+
elsif FFIBindings.zstd_version_number < 10104
|
62
|
+
frame_params = FFIBindings::ZSTD_frameParams.new
|
63
|
+
FFIBindings.zstd_get_frame_params(frame_params, string, string.bytesize)
|
64
|
+
frame_params[:frameContentSize]
|
65
|
+
else
|
66
|
+
FFIBindings.zstd_find_decompressed_size(string, string.bytesize)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.simple_compress(string, options = {})
|
71
|
+
level = options[:level] || 0 # 0 means default
|
72
|
+
|
73
|
+
dst_size = FFIBindings.zstd_compress_bound(string.bytesize)
|
74
|
+
dst = FFI::MemoryPointer.new(:char, dst_size)
|
75
|
+
|
76
|
+
error_code = number_of_bytes = FFIBindings.zstd_compress(dst, dst_size, string, string.bytesize, level)
|
77
|
+
|
78
|
+
if FFIBindings.zstd_is_error(error_code) >= 0
|
79
|
+
dst.read_bytes(number_of_bytes)
|
80
|
+
else
|
81
|
+
raise "error"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.simple_decompress(string, options = {})
|
86
|
+
#
|
87
|
+
# The docs state, that one should be carefull when using the simple decompress API, because
|
88
|
+
# it relies on the upfront knowledge of the decompressed (dst) size. This information may
|
89
|
+
# by present within the frame header (or not). If it's present, it can be very large and/or
|
90
|
+
# intentionally modified, so it's vital to check that this value is within the systems limits
|
91
|
+
# and fallback to streaming decompression if unsure.
|
92
|
+
#
|
93
|
+
dst = FFI::MemoryPointer.new(:char, dst_size = API.decompressed_size(string))
|
94
|
+
error_code = number_of_bytes = FFIBindings.zstd_decompress(dst, dst_size, string, string.bytesize)
|
95
|
+
|
96
|
+
if FFIBindings.zstd_is_error(error_code) != 0
|
97
|
+
raise FFIBindings.zstd_get_error_name(error_code).read_string
|
98
|
+
else
|
99
|
+
dst.read_bytes(number_of_bytes)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.window_size(string)
|
104
|
+
if FFIBindings.zstd_version_number < 600
|
105
|
+
parameters = FFIBindings::ZSTD_parameters.new
|
106
|
+
FFIBindings.zstd_get_frame_params(parameters, string, string.bytesize)
|
107
|
+
2 ** parameters[:windowLog]
|
108
|
+
elsif FFIBindings.zstd_version_number < 700
|
109
|
+
frame_params = FFIBindings::ZSTD_frameParams.new
|
110
|
+
FFIBindings.zstd_get_frame_params(frame_params, string, string.bytesize)
|
111
|
+
2 ** frame_params[:windowLog]
|
112
|
+
elsif Zstandard::FFIBindings.zstd_version_number < 10300
|
113
|
+
frame_params = FFIBindings::ZSTD_frameParams.new
|
114
|
+
FFIBindings.zstd_get_frame_params(frame_params, string, string.bytesize)
|
115
|
+
frame_params[:windowSize]
|
116
|
+
else
|
117
|
+
frame_header = FFIBindings::ZSTD_frameHeader.new
|
118
|
+
FFIBindings.zstd_get_frame_header(frame_header, string, string.bytesize)
|
119
|
+
frame_header[:windowSize]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Zstandard
|
2
|
+
module Config
|
3
|
+
LIBRARY_PATH = ENV["ZSTANDARD_LIBRARY"] || "zstd"
|
4
|
+
|
5
|
+
# Threshold for switching to streaming decompression
|
6
|
+
MAX_SIMPLE_DECOMPRESS_SIZE =
|
7
|
+
begin
|
8
|
+
default = 1024 * 1024 * 32
|
9
|
+
env_param = ENV["ZSTANDARD_MAX_SIMPLE_DECOMPRESS_SIZE"].to_i
|
10
|
+
env_param > 0 ? env_param : default
|
11
|
+
end
|
12
|
+
.freeze
|
13
|
+
|
14
|
+
# Caps the window size of compressed data to prohibit abuse (e.g. by
|
15
|
+
# manipulated frame headers). The docs propose to support at least 8MB.
|
16
|
+
MAX_STREAMING_DECOMRPESS_BUFFER_SIZE =
|
17
|
+
begin
|
18
|
+
default = 1024 * 1024 * 8
|
19
|
+
env_param = ENV["ZSTANDARD_MAX_STREAMING_DECOMRPESS_BUFFER_SIZE"].to_i
|
20
|
+
env_param > 0 ? env_param : default
|
21
|
+
end
|
22
|
+
.freeze
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
require "ffi"
|
2
|
+
require_relative "./config"
|
3
|
+
|
4
|
+
module Zstandard
|
5
|
+
module FFIBindings
|
6
|
+
extend FFI::Library
|
7
|
+
begin
|
8
|
+
ffi_lib Config::LIBRARY_PATH
|
9
|
+
rescue LoadError => e
|
10
|
+
STDERR.puts "Could not open #{Config::LIBRARY_PATH} shared library!"
|
11
|
+
STDERR.puts
|
12
|
+
STDERR.puts "Please be sure you have zstd installed. This can be accomplished via"
|
13
|
+
STDERR.puts "- your systems package management (e.g. apt-get install zstd)"
|
14
|
+
STDERR.puts "- compiled/installed by yourself (use ZSTANDARD_LIBRARY env variable for non-default paths)"
|
15
|
+
STDERR.puts
|
16
|
+
raise e
|
17
|
+
end
|
18
|
+
|
19
|
+
# this is placed upfront because it's needed for various conditionals
|
20
|
+
attach_function :zstd_version_number, :ZSTD_versionNumber, [], :uint
|
21
|
+
|
22
|
+
raise Zstandard::LibraryVersionNotSupportedError if zstd_version_number < 500
|
23
|
+
|
24
|
+
#
|
25
|
+
# @!group Simple API
|
26
|
+
#
|
27
|
+
|
28
|
+
# @!method self.zstd_compress(dst, dstCapacity, src, srcSize, compressionLevel)
|
29
|
+
# @param [void*] dst
|
30
|
+
# @param [size_t] dstCapacity
|
31
|
+
# @param [const void*] src
|
32
|
+
# @param [size_t] srcSize
|
33
|
+
# @param [int] compressionLevel
|
34
|
+
# @return [size_t] the compressed size
|
35
|
+
attach_function :zstd_compress, :ZSTD_compress, [:pointer, :size_t, :pointer, :size_t, :int], :size_t
|
36
|
+
|
37
|
+
|
38
|
+
# @!method self.zstd_compress_bound(srcSize)
|
39
|
+
# @param [size_t] srcSize
|
40
|
+
# @return [size_t]
|
41
|
+
attach_function :zstd_compress_bound, :ZSTD_compressBound, [:size_t], :size_t
|
42
|
+
|
43
|
+
# @!method self.zstd_decompress(dst, dstCapacity, src, compressedSize)
|
44
|
+
# @param [void*] dst
|
45
|
+
# @param [size_t] dstCapacity
|
46
|
+
# @param [const void*] src
|
47
|
+
# @param [size_t] compressedSize
|
48
|
+
# @return [size_t] the decompressed size
|
49
|
+
attach_function :zstd_decompress, :ZSTD_decompress, [:pointer, :size_t, :pointer, :size_t], :size_t
|
50
|
+
|
51
|
+
#
|
52
|
+
# @!group Buffer-less streaming decompression
|
53
|
+
#
|
54
|
+
|
55
|
+
# @!method self.zstd_create_dctx
|
56
|
+
# @return [ZSTD_DCtx*]
|
57
|
+
attach_function :zstd_create_dctx, :ZSTD_createDCtx, [], :pointer
|
58
|
+
|
59
|
+
# @!method self.zstd_decompress_begin(dctx)
|
60
|
+
# @param [ZSTD_DCtx*] dctx
|
61
|
+
# @return [size_t]
|
62
|
+
attach_function :zstd_decompress_begin, :ZSTD_decompressBegin, [:pointer], :size_t
|
63
|
+
|
64
|
+
# @!method self.zstd_decompress_continue(dctx, dst, dstCapacity, src, srcSize)
|
65
|
+
# @param [ZSTD_DCtx*] dctx
|
66
|
+
# @param [void*] dst
|
67
|
+
# @param [size_t] dstCapacity
|
68
|
+
# @param [const void*] src
|
69
|
+
# @param [size_t] srcSize
|
70
|
+
# @return [size_t]
|
71
|
+
attach_function :zstd_decompress_continue, :ZSTD_decompressContinue, [:pointer, :pointer, :size_t, :pointer, :size_t], :size_t
|
72
|
+
|
73
|
+
# @!method self.zstd_find_decompressed_size
|
74
|
+
# @param [const void*] src
|
75
|
+
# @param [size_t] srcSize
|
76
|
+
# @return [unsigned long long]
|
77
|
+
if zstd_version_number >= 10104
|
78
|
+
attach_function :zstd_find_decompressed_size, :ZSTD_findDecompressedSize, [:pointer, :size_t], :uint64
|
79
|
+
end
|
80
|
+
|
81
|
+
# @!method self.zstd_free_dctx(dctx)
|
82
|
+
# @param [ZSTD_DCtx*] dctx
|
83
|
+
# @return [size_t]
|
84
|
+
attach_function :zstd_free_dctx, :ZSTD_freeDCtx, [:pointer], :size_t
|
85
|
+
|
86
|
+
# @!method self.zstd_get_frame_params(fparamsPtr, src, srcSize)
|
87
|
+
# @param [ZSTD_parameters,ZSTD_frameParams] fparamsPtr (type depends on the version of `libzstd`, from `>= 0.6.0`, it's {ZSTD_frameParams}, before it's {ZSTD_parameters})
|
88
|
+
# @param [const void*] src
|
89
|
+
# @param [size_t] srcSize
|
90
|
+
# @return [size_t]
|
91
|
+
# size_t ZSTD_getFrameParams(ZSTD_frameParams* fparamsPtr, const void* src, size_t srcSize)
|
92
|
+
if zstd_version_number < 10300
|
93
|
+
attach_function :zstd_get_frame_params, :ZSTD_getFrameParams, [:pointer, :pointer, :size_t], :size_t
|
94
|
+
else
|
95
|
+
attach_function :zstd_get_frame_header, :ZSTD_getFrameHeader, [:pointer, :pointer, :size_t], :size_t
|
96
|
+
end
|
97
|
+
|
98
|
+
# @!method zstd_next_src_size_to_deompress(dctx)
|
99
|
+
# @param [ZSTD_DCtx*] dctx
|
100
|
+
# @return [size_t]
|
101
|
+
attach_function :zstd_next_src_size_to_deompress, :ZSTD_nextSrcSizeToDecompress, [:pointer], :size_t
|
102
|
+
|
103
|
+
#
|
104
|
+
# @!group Helpers
|
105
|
+
#
|
106
|
+
|
107
|
+
# @!method zstd_get_error_name(code)
|
108
|
+
# @param [size_t] code
|
109
|
+
# @return [const char*]
|
110
|
+
attach_function :zstd_get_error_name, :ZSTD_getErrorName, [:size_t], :pointer
|
111
|
+
|
112
|
+
# @!method zstd_is_error(code)
|
113
|
+
# @param [size_t] code
|
114
|
+
# @return [unsigned] an integer indicating if this is an error code. A value > 0 indicates `true`.
|
115
|
+
# unsigned ZSTD_isError(size_t code)
|
116
|
+
attach_function :zstd_is_error, :ZSTD_isError, [:size_t], :uint
|
117
|
+
|
118
|
+
# @!method zstd_version_number
|
119
|
+
# @return [uint]
|
120
|
+
|
121
|
+
# @!endgroup Helpers
|
122
|
+
|
123
|
+
#
|
124
|
+
# (Advanced) types (requires zstd_version_number to be attached for conditionals)
|
125
|
+
#
|
126
|
+
|
127
|
+
if zstd_version_number < 600
|
128
|
+
enum :ZSTD_strategy, [:ZSTD_fast, :ZSTD_greedy, :ZSTD_lazy, :ZSTD_lazy2, :ZSTD_btlazy2]
|
129
|
+
elsif zstd_version_number < 800
|
130
|
+
enum :ZSTD_strategy, [:ZSTD_fast, :ZSTD_greedy, :ZSTD_lazy, :ZSTD_lazy2, :ZSTD_btlazy2, :ZSTD_btopt]
|
131
|
+
else
|
132
|
+
enum :ZSTD_strategy, [:ZSTD_fast, :ZSTD_dfast, :ZSTD_greedy, :ZSTD_lazy, :ZSTD_lazy2, :ZSTD_btlazy2, :ZSTD_btopt]
|
133
|
+
end
|
134
|
+
|
135
|
+
# The format of this struct depends on the version of `libzstd` you are using.
|
136
|
+
#
|
137
|
+
# `<= v0.6.x`
|
138
|
+
# ```
|
139
|
+
# typedef struct {
|
140
|
+
# U64 frameContentSize;
|
141
|
+
# U32 windowLog;
|
142
|
+
# } ZSTD_frameParams;
|
143
|
+
# ```
|
144
|
+
#
|
145
|
+
# `>= v0.7.x`
|
146
|
+
# ```
|
147
|
+
# typedef struct {
|
148
|
+
# unsigned long long frameContentSize;
|
149
|
+
# unsigned windowSize;
|
150
|
+
# unsigned dictID;
|
151
|
+
# unsigned checksumFlag;
|
152
|
+
# } ZSTD_frameParams;
|
153
|
+
# ```
|
154
|
+
if Zstandard::FFIBindings.zstd_version_number < 10300
|
155
|
+
class ZSTD_frameParams < FFI::Struct
|
156
|
+
# @!method [](member)
|
157
|
+
if Zstandard::FFIBindings.zstd_version_number < 700
|
158
|
+
# `<= v0.6.x`
|
159
|
+
# @overload [](member)
|
160
|
+
# @param [:frameContentSize, :windowLog] member
|
161
|
+
layout(
|
162
|
+
frameContentSize: :uint64,
|
163
|
+
windowLog: :uint32
|
164
|
+
)
|
165
|
+
elsif Zstandard::FFIBindings.zstd_version_number < 800
|
166
|
+
# `>= v0.7.x`
|
167
|
+
# @overload [](member)
|
168
|
+
# @param [:frameContentSize, :windowSize, :dictID, :checksumFlag] member
|
169
|
+
layout(
|
170
|
+
:frameContentSize, :uint64,
|
171
|
+
:windowSize, :uint32,
|
172
|
+
:dictID, :uint32,
|
173
|
+
:checksumFlag, :uint32
|
174
|
+
)
|
175
|
+
else
|
176
|
+
layout(
|
177
|
+
:frameContentSize, :ulong_long,
|
178
|
+
:windowSize, :uint,
|
179
|
+
:dictID, :uint,
|
180
|
+
:checksumFlag, :uint
|
181
|
+
)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
else
|
185
|
+
class ZSTD_frameHeader < FFI::Struct
|
186
|
+
layout(
|
187
|
+
:frameContentSize, :ulong_long,
|
188
|
+
:windowSize, :uint,
|
189
|
+
:dictID, :uint,
|
190
|
+
:checksumFlag, :uint
|
191
|
+
)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class ZSTD_parameters < FFI::Struct
|
196
|
+
# @!method[](member)
|
197
|
+
if Zstandard::FFIBindings.zstd_version_number < 600
|
198
|
+
# `<= v0.5.x`
|
199
|
+
# @overload [](member)
|
200
|
+
# @param [:srcSize, :windowLog, :contentLog, :hashLog, :searchLog, :searchLength, :targetLength, :strategy]
|
201
|
+
layout(
|
202
|
+
:srcSize, :uint64,
|
203
|
+
:windowLog, :uint32,
|
204
|
+
:contentLog, :uint32,
|
205
|
+
:hashLog, :uint32,
|
206
|
+
:searchLog, :uint32,
|
207
|
+
:searchLength, :uint32,
|
208
|
+
:targetLength, :uint32,
|
209
|
+
:strategy, :ZSTD_strategy
|
210
|
+
)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
PROCESSOR_TYPE=$(uname -p)
|
4
|
+
DISTRIBUTOR_ID=$(lsb_release -i -s|tr '[:upper:]' '[:lower:]')
|
5
|
+
RELEASE=$(lsb_release -r -s)
|
6
|
+
|
7
|
+
LIBRARIES_DIRECTORY="./spec/libzstd/${PROCESSOR_TYPE}/${DISTRIBUTOR_ID}/${RELEASE}"
|
8
|
+
|
9
|
+
if [ -d "$LIBRARIES_DIRECTORY" ]; then
|
10
|
+
for library in ${LIBRARIES_DIRECTORY}/*
|
11
|
+
do
|
12
|
+
bundle exec rake ZSTANDARD_LIBRARY=$library
|
13
|
+
if [ "$?" -ne "0" ]; then
|
14
|
+
exit 1
|
15
|
+
fi
|
16
|
+
done
|
17
|
+
else
|
18
|
+
echo "There are no bundled libraries for the current system."
|
19
|
+
exit 1
|
20
|
+
fi
|
data/zstandard.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'zstandard/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "zstandard"
|
8
|
+
spec.version = Zstandard::VERSION
|
9
|
+
spec.authors = ["Michael Sievers"]
|
10
|
+
spec.email = ["michael_sievers@web.de"]
|
11
|
+
spec.summary = %q{Zstandard compression library bindings}
|
12
|
+
spec.homepage = "https://github.com/msievers/zstandard"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
|
+
f.match(%r{^(test|spec|features|bin|benchmarks|run_rspec_with_bundled_libraries.sh)/})
|
17
|
+
end
|
18
|
+
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_dependency "ffi", "~> 1.0"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zstandard
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Sievers
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- michael_sievers@web.de
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- ".rspec"
|
36
|
+
- ".travis.yml"
|
37
|
+
- ".yardopts"
|
38
|
+
- Gemfile
|
39
|
+
- LICENSE.txt
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- lib/zstandard.rb
|
43
|
+
- lib/zstandard/api.rb
|
44
|
+
- lib/zstandard/config.rb
|
45
|
+
- lib/zstandard/ffi_bindings.rb
|
46
|
+
- lib/zstandard/version.rb
|
47
|
+
- run_rspec_with_bundled_libraries.sh
|
48
|
+
- zstandard.gemspec
|
49
|
+
homepage: https://github.com/msievers/zstandard
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.6.11
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: Zstandard compression library bindings
|
73
|
+
test_files: []
|