zstandard 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/msievers/zstandard-ruby.svg?branch=master)](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: []
|