openzip 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []