openzip 0.1.1 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cf00f428db2855cde725e2a5ca4a347eeb627919
4
- data.tar.gz: 6d278070f52f7f01389cd3f1d1e73b2401cb62ce
3
+ metadata.gz: 8f451c024999099c3a92d2c1a2d8a4188f8579b9
4
+ data.tar.gz: 3a115ee87ca4b72a1c6343c82ddfa7b08a0e1a10
5
5
  SHA512:
6
- metadata.gz: b4b111c006a188cd75ff13dc26fa1ed3ed429076166e21343eb1d3db0db3252ccd5641e8c3af6d49894820724118c0a8760b4e5665afcc9de1c60c6def650719
7
- data.tar.gz: 3eaa5a0e80e6a6fca72b488cb2ff11c728ddabae60ce3b7a9ce8a41c1492bdc8fc944c7111eba7f8e6a280d97ae17e17b70f46b8cab79c6cbf2bb3a72a22feb9
6
+ metadata.gz: ebd30eaf00e393a0e76242e6a0884a37b30e790084e549b587cf090990dfc2250d74f9506f7cb8a4d7a44bcdb0c035e622ba3bff9b23cf821803fe8ec6d79396
7
+ data.tar.gz: 73ad5ee53c7102c29cc45b50694ab9248ed1c3df021c695e7b55d2e01f9211e44e272edb77079e1b65fb017ae6736f34ef7af6ea43bd779564ea1619daf1091d
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /spec/reports/
10
10
  /tmp/
11
11
  /source/target/
12
+ /benchmarking/files/
data/.rubocop.yml CHANGED
@@ -32,6 +32,10 @@ Metrics/LineLength:
32
32
  Description: 'Limit lines to 120 characters.'
33
33
  Max: 120
34
34
 
35
+ Metrics/BlockLength:
36
+ Exclude:
37
+ - spec/**/*.rb
38
+
35
39
  Lint/EndAlignment:
36
40
  EnforcedStyleAlignWith: variable
37
41
  SupportedStylesAlignWith:
@@ -54,3 +58,9 @@ Style/StringMethods:
54
58
  RSpec/MessageSpies:
55
59
  Enabled: true
56
60
  EnforcedStyle: receive
61
+
62
+ RSpec/MultipleExpectations:
63
+ Enabled: false
64
+
65
+ RSpec/NestedGroups:
66
+ Enabled: false
data/.simplecov CHANGED
@@ -1,3 +1,4 @@
1
1
  SimpleCov.start do
2
- add_filter "spec"
2
+ add_filter ".bundle"
3
+ add_filter "vendor"
3
4
  end
data/.travis.yml CHANGED
@@ -2,11 +2,19 @@ language: ruby
2
2
  cache: bundler
3
3
  sudo: required
4
4
 
5
+ os:
6
+ - linux
7
+ - osx
8
+
5
9
  rvm:
6
- - 2.3.0
10
+ - 2.0.0
11
+ - 2.1.10
12
+ - 2.2.5
13
+ - 2.3.3
14
+ - 2.4.0
7
15
 
8
16
  before_install:
9
- - gem install bundler -v 1.13.6
17
+ - gem install bundler
10
18
  - bin/compile
11
19
 
12
20
  script:
data/Gemfile CHANGED
@@ -9,6 +9,7 @@ group :development, :test do
9
9
  end
10
10
 
11
11
  group :test do
12
+ gem "climate_control"
12
13
  gem "codeclimate-test-reporter", "~> 1.0.0", require: false
13
14
  gem "simplecov", require: false
14
15
  end
data/README.md CHANGED
@@ -6,23 +6,42 @@
6
6
  [![Gem](https://img.shields.io/gem/v/openzip.svg?style=flat-square)](https://github.com/ilyasgaraev/openzip)
7
7
 
8
8
 
9
- **Openzip** is a Ruby library for fast extract Zip files.
9
+ **Openzip** is a Ruby library (written in Rust) for fast extract Zip files.
10
10
 
11
11
  ## Usage
12
12
 
13
+ Before you begin, you need to install Rust (with Cargo) on your system (see [Requirements](#requirements)).
14
+
13
15
  ```ruby
14
- path_to_zip = "path/to/zipfile.zip"
15
- extract_to_path = "path/to/extract"
16
+ Openzip.extract("path/to/file.zip", "extract/path")
17
+ # returns true if the method successfully executed; otherwise, false
18
+ ```
16
19
 
17
- Openzip.extract(path_to_zip, extract_to_path)
20
+ The **DEBUG** environment variable can be used to enable debug logs:
21
+
22
+ ```ruby
23
+ # DEBUG=true
24
+ Openzip.extract("wrong/path/file.zip", "extract/path")
25
+ # Error: No such file or directory (os error 2)
26
+ # => false
18
27
  ```
19
28
 
29
+ ## Requirements
30
+
31
+ * Rust and Cargo ([https://www.rust-lang.org/en-US/install.html](https://www.rust-lang.org/en-US/install.html))
32
+ * Ruby 2.0 or greater
33
+
34
+ ## Benchmarking
35
+
36
+ * Memory usage: [benchmarking/memory.md](benchmarking/memory.md)
37
+ * Iterations per second: [benchmarking/iterations.md](benchmarking/iterations.md)
38
+
20
39
  ## Installation
21
40
 
22
41
  Add this line to your application's Gemfile:
23
42
 
24
43
  ```ruby
25
- gem 'openzip'
44
+ gem "openzip"
26
45
  ```
27
46
 
28
47
  And then execute:
@@ -0,0 +1,48 @@
1
+ # Iterations per second
2
+
3
+ **Openzip** vs **rubyzip**.
4
+
5
+ ---
6
+ ## Zip file
7
+
8
+ ### File info
9
+ * **Type:** zip
10
+ * **Files in archive:** 2500
11
+ * **Size:** 6 500 079 bytes (6,5 MB)
12
+
13
+ ### Results
14
+
15
+ ```
16
+ Warming up --------------------------------------
17
+ openzip 1.000 i/100ms
18
+ rubyzip 1.000 i/100ms
19
+ Calculating -------------------------------------
20
+ openzip 0.841 (± 0.0%) i/s - 5.000 in 5.943962s
21
+ rubyzip 0.369 (± 0.0%) i/s - 2.000 in 5.416802s
22
+
23
+ Comparison:
24
+ openzip: 0.8 i/s
25
+ rubyzip: 0.4 i/s - 2.28x slower
26
+ ```
27
+
28
+ ---
29
+ ## Excel file
30
+
31
+ ### File info
32
+ * **Type:** xlsx
33
+ * **Files in archive:** 13
34
+ * **Size:** 775 231 bytes (778 KB)
35
+ * **Filled rows:** 5000
36
+
37
+ ```
38
+ Warming up --------------------------------------
39
+ openzip 1.000 i/100ms
40
+ rubyzip 1.000 i/100ms
41
+ Calculating -------------------------------------
42
+ openzip 18.270 (± 5.5%) i/s - 91.000 in 5.000976s
43
+ rubyzip 13.548 (± 7.4%) i/s - 68.000 in 5.033290s
44
+
45
+ Comparison:
46
+ openzip: 18.3 i/s
47
+ rubyzip: 13.5 i/s - 1.35x slower
48
+ ```
@@ -0,0 +1,50 @@
1
+ # Memory usage
2
+
3
+ **Openzip** vs **rubyzip**.
4
+
5
+ ---
6
+ ## Zip file
7
+
8
+ ### File info
9
+ * **Type:** zip
10
+ * **Files in archive:** 2500
11
+ * **Size:** 6 500 079 bytes (6,5 MB)
12
+
13
+ ### Results
14
+
15
+ ```
16
+ Calculating -------------------------------------
17
+ openzip 627.000 memsize ( 40.000 retained)
18
+ 10.000 objects ( 1.000 retained)
19
+ 3.000 strings ( 0.000 retained)
20
+ rubyzip 304.757M memsize ( 120.000 retained)
21
+ 866.162k objects ( 3.000 retained)
22
+ 50.000 strings ( 2.000 retained)
23
+
24
+ Comparison:
25
+ openzip: 627 allocated
26
+ rubyzip: 304757473 allocated - 486056.58x more
27
+ ```
28
+
29
+ ---
30
+ ## Excel file
31
+
32
+ ### File info
33
+ * **Type:** xlsx
34
+ * **Files in archive:** 13
35
+ * **Size:** 775 231 bytes (778 KB)
36
+ * **Filled rows:** 5000
37
+
38
+ ```
39
+ Calculating -------------------------------------
40
+ openzip 626.000 memsize ( 40.000 retained)
41
+ 10.000 objects ( 1.000 retained)
42
+ 3.000 strings ( 0.000 retained)
43
+ rubyzip 62.955M memsize ( 40.000 retained)
44
+ 2.763k objects ( 1.000 retained)
45
+ 50.000 strings ( 0.000 retained)
46
+
47
+ Comparison:
48
+ openzip: 626 allocated
49
+ rubyzip: 62954673 allocated - 100566.57x more
50
+ ```
@@ -1,3 +1,3 @@
1
1
  module Openzip
2
- VERSION = "0.1.1".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
data/lib/openzip.rb CHANGED
@@ -5,11 +5,17 @@ module Openzip
5
5
  extend Fiddle::Importer
6
6
 
7
7
  def self.lib_ext
8
- raise "Sorry, windows is not supported yet" if RUBY_PLATFORM =~ /win32/
8
+ raise NotImplementedError, "Sorry, Windows is not supported" if RUBY_PLATFORM =~ /win32/
9
9
 
10
10
  RUBY_PLATFORM =~ /darwin/ ? "dylib" : "so"
11
11
  end
12
12
 
13
13
  dlload "#{File.dirname(__FILE__)}/../source/target/release/libopenzip.#{lib_ext}"
14
- extern "void extract(char*, char*)"
14
+
15
+ def self.extract(zippath, outdirpath)
16
+ extract_rust(zippath, outdirpath) != 0
17
+ end
18
+
19
+ extern "char extract_rust(char*, char*)"
20
+ private_class_method :extract_rust
15
21
  end
data/openzip.gemspec CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Ilyas Garaev"]
10
10
  spec.email = ["vearagi@gmail.com"]
11
11
 
12
- spec.summary = "Openzip is a Ruby library for fast reading Zip files."
13
- spec.description = "Openzip is a Ruby library for fast reading Zip files."
12
+ spec.summary = "Openzip is a Ruby library (written in Rust) for fast reading Zip files."
13
+ spec.description = "Openzip is a Ruby library (written in Rust) for fast reading Zip files."
14
14
  spec.homepage = "https://github.com/ilyasgaraev/openzip"
15
15
  spec.license = "MIT"
16
16
 
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.extensions = Dir["source/extconf.rb"]
22
22
 
23
+ spec.required_ruby_version = "~> 2.0"
24
+
23
25
  spec.add_development_dependency "bundler", "~> 1.13"
24
26
  spec.add_development_dependency "pry", "~> 0.10"
25
27
  spec.add_development_dependency "rake", "~> 10.0"
data/source/extconf.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  unless system("cargo --version") || system("rustc --version")
2
- raise "You have to install Rust with Cargo (https://www.rust-lang.org/)"
2
+ raise SystemCallError, "You have to install Rust with Cargo (https://www.rust-lang.org/en-US/install.html)"
3
3
  end
@@ -0,0 +1,93 @@
1
+ extern crate zip;
2
+
3
+ use std::io;
4
+ use std::fs;
5
+ use std::path::{Path, PathBuf, Component};
6
+ use std::error::Error;
7
+
8
+ #[cfg(unix)]
9
+ use std::os::unix::fs::PermissionsExt;
10
+
11
+ pub fn run(zippath: &Path, outdirpath: &Path) -> Result<bool, Box<Error>>
12
+ {
13
+ let file = try!(fs::File::open(&zippath));
14
+ try!(create_directory(&outdirpath, None));
15
+
16
+ let mut archive = try!(zip::ZipArchive::new(file));
17
+
18
+ for i in 0..archive.len()
19
+ {
20
+ let mut file = archive.by_index(i).unwrap();
21
+ let outpath = sanitize_filename(file.name(), outdirpath);
22
+
23
+ try!(create_directory(outpath.parent().unwrap_or(Path::new("")), None));
24
+
25
+ let perms = convert_permissions(file.unix_mode());
26
+
27
+ if file.name().ends_with("/") {
28
+ try!(create_directory(&outpath, perms));
29
+ } else {
30
+ try!(write_file(&mut file, &outpath, perms));
31
+ }
32
+ }
33
+
34
+ Ok(true)
35
+ }
36
+
37
+ #[cfg(unix)]
38
+ fn convert_permissions(mode: Option<u32>) -> Option<fs::Permissions>
39
+ {
40
+ match mode {
41
+ Some(mode) => Some(fs::Permissions::from_mode(mode)),
42
+ None => None,
43
+ }
44
+ }
45
+
46
+ #[cfg(not(unix))]
47
+ fn convert_permissions(_mode: Option<u32>) -> Option<fs::Permissions>
48
+ {
49
+ None
50
+ }
51
+
52
+ fn write_file(file: &mut zip::read::ZipFile, outpath: &Path, perms: Option<fs::Permissions>) -> Result<(), Box<Error>>
53
+ {
54
+ let mut outfile = try!(fs::File::create(&outpath));
55
+ try!(io::copy(file, &mut outfile));
56
+
57
+ if let Some(perms) = perms {
58
+ try!(fs::set_permissions(outpath, perms));
59
+ }
60
+
61
+ Ok(())
62
+ }
63
+
64
+ fn create_directory(outpath: &Path, perms: Option<fs::Permissions>) -> Result<(), Box<Error>>
65
+ {
66
+ try!(fs::create_dir_all(&outpath));
67
+
68
+ if let Some(perms) = perms {
69
+ try!(fs::set_permissions(outpath, perms));
70
+ }
71
+
72
+ Ok(())
73
+ }
74
+
75
+ fn sanitize_filename(filename: &str, outdir: &Path) -> PathBuf
76
+ {
77
+ let no_null_filename = match filename.find('\0') {
78
+ Some(index) => &filename[0..index],
79
+ None => filename,
80
+ };
81
+
82
+ let filepath = Path::new(no_null_filename)
83
+ .components()
84
+ .filter(|component| *component != Component::ParentDir)
85
+ .fold(PathBuf::new(), |mut path, ref cur| {
86
+ path.push(cur.as_os_str());
87
+ path
88
+ });
89
+
90
+ let mut outdirbuf = PathBuf::from(outdir);
91
+ outdirbuf.push(filepath);
92
+ return outdirbuf.to_path_buf();
93
+ }
data/source/src/lib.rs CHANGED
@@ -1,94 +1,30 @@
1
- extern crate zip;
2
1
  extern crate libc;
3
2
 
4
- use std::io;
5
- use std::fs;
6
- use std::path::{Path, PathBuf, Component};
7
- use std::ffi::CStr;
3
+ mod extractor;
8
4
 
9
- #[cfg(unix)]
10
- use std::os::unix::fs::PermissionsExt;
5
+ use std::env;
6
+ use std::path::Path;
7
+ use std::ffi::CStr;
11
8
 
12
9
  #[no_mangle]
13
- pub extern fn extract(zippath: *const libc::c_char, outdirpath: *const libc::c_char) {
14
- let zname = Path::new(rust_string(zippath));
15
- let outdir = Path::new(rust_string(outdirpath));
16
-
17
- create_directory(&outdir, None);
18
- let file = fs::File::open(&zname).unwrap();
19
-
20
- let mut archive = zip::ZipArchive::new(file).unwrap();
21
-
22
- for i in 0..archive.len()
23
- {
24
- let mut file = archive.by_index(i).unwrap();
25
- let outpath = sanitize_filename(file.name(), outdir);
26
-
27
- create_directory(outpath.parent().unwrap_or(Path::new("")), None);
28
-
29
- let perms = convert_permissions(file.unix_mode());
30
-
31
- if (&*file.name()).ends_with("/") {
32
- create_directory(&outpath, perms);
33
- }
34
- else {
35
- write_file(&mut file, &outpath, perms);
36
- }
37
- }
38
- }
39
-
40
- fn rust_string(r_string: *const libc::c_char) -> &'static str {
41
- unsafe { CStr::from_ptr(r_string) }.to_str().unwrap()
42
- }
43
-
44
- #[cfg(unix)]
45
- fn convert_permissions(mode: Option<u32>) -> Option<fs::Permissions>
10
+ pub extern fn extract_rust(zip: *const libc::c_char, outdir: *const libc::c_char) -> bool
46
11
  {
47
- match mode {
48
- Some(mode) => Some(fs::Permissions::from_mode(mode)),
49
- None => None,
50
- }
51
- }
12
+ let zippath = Path::new(rust_string(zip));
13
+ let outdirpath = Path::new(rust_string(outdir));
52
14
 
53
- #[cfg(not(unix))]
54
- fn convert_permissions(_mode: Option<u32>) -> Option<fs::Permissions>
55
- {
56
- None
57
- }
58
-
59
- fn write_file(file: &mut zip::read::ZipFile, outpath: &Path, perms: Option<fs::Permissions>)
60
- {
61
- let mut outfile = fs::File::create(&outpath).unwrap();
62
- io::copy(file, &mut outfile).unwrap();
63
- if let Some(perms) = perms {
64
- fs::set_permissions(outpath, perms).unwrap();
65
- }
66
- }
15
+ return match extractor::run(zippath, outdirpath) {
16
+ Ok(_) => true,
17
+ Err(e) => {
18
+ if env::var("DEBUG").ok() == Some(String::from("true")) {
19
+ println!("Error: {}", e);
20
+ }
67
21
 
68
- fn create_directory(outpath: &Path, perms: Option<fs::Permissions>)
69
- {
70
- fs::create_dir_all(&outpath).unwrap();
71
- if let Some(perms) = perms {
72
- fs::set_permissions(outpath, perms).unwrap();
22
+ false
23
+ }
73
24
  }
74
25
  }
75
26
 
76
- fn sanitize_filename(filename: &str, outdir: &Path) -> PathBuf
27
+ fn rust_string(r_string: *const libc::c_char) -> &'static str
77
28
  {
78
- let no_null_filename = match filename.find('\0') {
79
- Some(index) => &filename[0..index],
80
- None => filename,
81
- };
82
-
83
- let filepath = Path::new(no_null_filename)
84
- .components()
85
- .filter(|component| *component != Component::ParentDir)
86
- .fold(PathBuf::new(), |mut path, ref cur| {
87
- path.push(cur.as_os_str());
88
- path
89
- });
90
-
91
- let mut outdirbuf = PathBuf::from(outdir);
92
- outdirbuf.push(filepath);
93
- return outdirbuf.to_path_buf();
29
+ unsafe { CStr::from_ptr(r_string) }.to_str().unwrap()
94
30
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openzip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ilyas Garaev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-15 00:00:00.000000000 Z
11
+ date: 2017-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,7 +66,7 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
- description: Openzip is a Ruby library for fast reading Zip files.
69
+ description: Openzip is a Ruby library (written in Rust) for fast reading Zip files.
70
70
  email:
71
71
  - vearagi@gmail.com
72
72
  executables: []
@@ -84,6 +84,8 @@ files:
84
84
  - LICENSE
85
85
  - README.md
86
86
  - Rakefile
87
+ - benchmarking/iterations.md
88
+ - benchmarking/memory.md
87
89
  - bin/bundle-audit
88
90
  - bin/ci
89
91
  - bin/compile
@@ -100,6 +102,7 @@ files:
100
102
  - source/Cargo.toml
101
103
  - source/Makefile
102
104
  - source/extconf.rb
105
+ - source/src/extractor.rs
103
106
  - source/src/lib.rs
104
107
  homepage: https://github.com/ilyasgaraev/openzip
105
108
  licenses:
@@ -111,9 +114,9 @@ require_paths:
111
114
  - lib
112
115
  required_ruby_version: !ruby/object:Gem::Requirement
113
116
  requirements:
114
- - - ">="
117
+ - - "~>"
115
118
  - !ruby/object:Gem::Version
116
- version: '0'
119
+ version: '2.0'
117
120
  required_rubygems_version: !ruby/object:Gem::Requirement
118
121
  requirements:
119
122
  - - ">="
@@ -124,5 +127,5 @@ rubyforge_project:
124
127
  rubygems_version: 2.6.8
125
128
  signing_key:
126
129
  specification_version: 4
127
- summary: Openzip is a Ruby library for fast reading Zip files.
130
+ summary: Openzip is a Ruby library (written in Rust) for fast reading Zip files.
128
131
  test_files: []