faster_path 0.1.8 → 0.1.10

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: 8ad592a885912e4267151be6ee15a9accfbd34ef
4
- data.tar.gz: b2b22d3198284b6dc2bb177c72bd1c99bbcbb3a5
3
+ metadata.gz: 40367b49892754c5b9d83891289cc0ce30e757d6
4
+ data.tar.gz: 8a92f9a7796c946a873cfded321b529f1f8d57bf
5
5
  SHA512:
6
- metadata.gz: 17eecf18612f14fa87de8fc58f2ae4b08cb4e7f98e4d62283f87457d09e1822a3aba7db72df2e14e4d9cddcab60abf7cc2148742d9ae301541454e7a62080201
7
- data.tar.gz: 15ad8f6310ce3b5188533f957bbc5e0ba9280686dd148adca124d455249c6426b2ee1f4b9cff43383e6601daf5682fad78a0d376d343537fe1f81118a20fcb21
6
+ metadata.gz: 8dc854ddd64ba12ac79ff11e792bf5fc6723d3ea547056d474a344572e0608f0e0c0c0d688c39a70c50e5a636d36215d0906a952cd65f58402a7fc05d8f135c4
7
+ data.tar.gz: f0e6009a18eeeb0414836f3b13d4684db44839e466845b745c6b08440da63ad247be78acc7671ef53c07dc8c69237d0e964e81c3bd9bb9dd717ce17a65fd2184
data/Cargo.lock CHANGED
@@ -10,3 +10,5 @@ name = "libc"
10
10
  version = "0.2.11"
11
11
  source = "registry+https://github.com/rust-lang/crates.io-index"
12
12
 
13
+ [metadata]
14
+ "checksum libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c96061f0c8a2dc27482e394d82e23073569de41d73cd736672ccd3e5c7471bfd"
data/Gemfile CHANGED
@@ -1,4 +1,12 @@
1
- source 'https://rubygems.org'
1
+ source 'https://rubygems.org' do
2
+ # Specify your gem's dependencies in faster_path.gemspec
3
+ gemspec
4
+ end
2
5
 
3
- # Specify your gem's dependencies in faster_path.gemspec
4
- gemspec
6
+ begin
7
+ # https://github.com/ruby/spec dependencies
8
+ eval_gemfile File.expand_path('spec/ruby_spec/Gemfile', File.dirname(__FILE__))
9
+ rescue
10
+ `git submodule update --init`
11
+ eval_gemfile File.expand_path('spec/ruby_spec/Gemfile', File.dirname(__FILE__))
12
+ end
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
  [![Gem Version](https://badge.fury.io/rb/faster_path.svg)](https://badge.fury.io/rb/faster_path)
3
3
  [![Build Status](https://travis-ci.org/danielpclark/faster_path.svg?branch=master)](https://travis-ci.org/danielpclark/faster_path)
4
4
  [![Tweet This](https://raw.githubusercontent.com/danielpclark/faster_path/master/assets/tweet.png)](https://twitter.com/share?url=https%3A%2F%2Fgithub.com%2Fdanielpclark%2Ffaster_path&via=6ftdan&hashtags=Ruby&text=You%20could%20save%2015%25%20or%20more%20on%20website%20page%20load%20time%20by%20switching%20to%20the%20FasterPath%20gem.)
5
+ [New Wiki Up!](https://github.com/danielpclark/faster_path/wiki)
5
6
 
6
7
  #### This gem shaves off more than 30% of my Rails application page load time.
7
8
 
@@ -116,25 +117,33 @@ And then execute:
116
117
  Or install it yourself as:
117
118
 
118
119
  $ gem install faster_path
119
-
120
+
120
121
  **MAC USERS:** At the moment Mac users need to install the extension manualy. Go to the gem directory and run `cargo build --release` . There is an issue opened for this and I'm looking for people who have Macs to help on this.
121
122
 
123
+ ## Visual Benchmarks
124
+
125
+ Benchmarks in Faster Path now produce visual graph charts of performance improvements.
126
+ When you run `rake bench` the graph art will be placed in `doc/graph/`. Here's the performance
127
+ improvement result for the `chop_basename` method.
128
+
129
+ ![Visual Benchmark](https://raw.githubusercontent.com/danielpclark/faster_path/master/assets/chop_basename_benchmark.jpg "Visual Benchmark")
130
+
122
131
  ## Usage
123
132
 
124
133
  Current methods implemented:
125
134
 
126
135
  |FasterPath Rust Implementation|Ruby 2.3.1 Implementation|Performance Improvement|
127
136
  |---|---|:---:|
128
- | `FasterPath.absolute?` | `Pathname#absolute?` | 1234.6% |
129
- | `FasterPath.basename` | `File.basename` | 31.3% |
130
- | `FasterPath.chop_basename` | `Pathname#chop_basename` | 66.0% |
131
- | `FasterPath.relative?` | `Pathname#relative?` | 1262.3% |
137
+ | `FasterPath.absolute?` | `Pathname#absolute?` | 93.9% |
138
+ | `FasterPath.chop_basename` | `Pathname#chop_basename` | 50.6% |
139
+ | `FasterPath.relative?` | `Pathname#relative?` | 93.2% |
132
140
  | `FasterPath.blank?` | | |
133
- | `FasterPath.directory?` | `Pathname#directory?` | 20% |
134
- | `FasterPath.add_trailing_separator` | `Pathname#add_trailing_separator` | 63.8% |
141
+ | `FasterPath.directory?` | `Pathname#directory?` | 25.5% |
142
+ | `FasterPath.add_trailing_separator` | `Pathname#add_trailing_separator` | 46.8% |
143
+ | `FasterPath.has_trailing_separator?` | `Pathname#has_trailing_separator` | 61.2% |
135
144
 
136
145
  You may choose to use the methods directly, or scope change to rewrite behavior on the
137
- standard library with the included refinements, or even call a method to monkeypatch
146
+ standard library with the included refinements, or even call a method to monkeypatch
138
147
  everything everywhere.
139
148
 
140
149
  **Note:** `Pathname#chop_basename` in Ruby STDLIB has a bug with blank strings, that is the
@@ -154,6 +163,21 @@ require "faster_path/optional/monkeypatches"
154
163
  FasterPath.sledgehammer_everything!
155
164
  ```
156
165
 
166
+ ## Unstable optional bits
167
+
168
+ **Optional methods which ~~have regressions.~~ are unstable.** These will **not** be included by default in monkey-patches. Be cautious when using the `FasterPath::RefineFile` refinement. To try them anyways use the environment flag of `WITH_REGRESSION`. These methods are here to be improved upon.
169
+
170
+ |FasterPath Implementation|Ruby Implementation|
171
+ |---|---|
172
+ | `FasterPath.dirname` | `File.dirname` |
173
+ | `FasterPath.basename` | `File.basename` |
174
+ | `FasterPath.extname` | `File.extname` |
175
+
176
+ It's been my observation (and some others) that the Rust implementation of the C code for `File` has similar results but
177
+ performance seems to vary based on CPU cache on possibly 64bit/32bit system environmnets.
178
+
179
+ **Developers for FasterPath:** Most of these need to be rewritten, please use `WITH_REGRESSION` in your testing. You can see the resulting failures currently on TravisCI under "Allow Failures".
180
+
157
181
  ## Getting Started with Development
158
182
 
159
183
  The primary methods to target are mostly listed in the **Why** section above. You may find the Ruby
@@ -166,8 +190,46 @@ Methods will be written as exclusively in Rust as possible. Even just writing a
166
190
  Rust method like `!absolute?` _(not absolute)_ drops 39% of the performance already gained in Rust.
167
191
  Whenever feasible implement it in Rust.
168
192
 
169
- After checking out the repo, make sure you have Rust installed. Then run `bundle && rake build_lib` .
170
- Then, run `rake test` to run the tests, and `rake bench` for benchmarks.
193
+ After checking out the repo, make sure you have Rust installed, then run `bundle`.
194
+ Run `rake test` to run the tests, and `rake bench` for benchmarks.
195
+
196
+ Learn and share performance tips on the [wiki](https://github.com/danielpclark/faster_path/wiki)!
197
+
198
+ ### Building and running tests
199
+
200
+ First, bundle the gem's development dependencies by running `bundle`.
201
+
202
+ Then, build the rust code:
203
+
204
+ ```sh
205
+ rake build_src
206
+ ```
207
+
208
+ FasterPath is tested with [The Ruby Spec Suite](https://github.com/ruby/spec) to ensure it is compatible with the
209
+ native implementation, and also has its own test suite testing its monkey-patching and refinements functionality.
210
+
211
+ To run all the tests at once, simply run `rake`.
212
+ To run all the ruby spec tests, run `mspec`.
213
+
214
+ To run an individual test or benchmark from FasterPath's own suite:
215
+
216
+ ```sh
217
+ # An individual test file:
218
+ ruby -I lib:test test/benches/absolute_benchmark.rb
219
+ # All tests:
220
+ rake minitest
221
+ ```
222
+
223
+ To run an individual ruby spec test, run `mspec` with a path relative to `spec/ruby_spec`, e.g.:
224
+
225
+ ```sh
226
+ # A path to a file or a directory:
227
+ mspec core/file/basename_spec.rb
228
+ # Tests most relevant to FasterPath:
229
+ mspec core/file library/pathname
230
+ # All tests:
231
+ mspec
232
+ ```
171
233
 
172
234
  ## Contributing
173
235
 
data/Rakefile CHANGED
@@ -2,13 +2,13 @@ require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
  require 'fileutils'
4
4
 
5
- desc "Building extension..."
5
+ desc "Build Rust extension"
6
6
  task :build_src do
7
7
  puts "Building extension..."
8
8
  system("cargo build --release")
9
9
  end
10
10
 
11
- desc "Cleaning up build..."
11
+ desc "Clean up Rust build"
12
12
  task :clean_src do
13
13
  puts "Cleaning up build..."
14
14
  # Remove all but library file
@@ -22,20 +22,29 @@ task :clean_src do
22
22
  )
23
23
  end
24
24
 
25
- desc "Compiling Rust extension..."
26
- task :build_lib => [:build_src, :clean_src] do
25
+ desc "Build + clean up Rust extension"
26
+ task build_lib: [:build_src, :clean_src] do
27
27
  puts "Completed build!"
28
28
  end
29
29
 
30
- Rake::TestTask.new(:test) do |t|
30
+ Rake::TestTask.new(minitest: :build_lib) do |t|
31
31
  t.libs << "test"
32
32
  t.libs << "lib"
33
33
  t.test_files = FileList['test/**/*_test.rb']
34
34
  end
35
35
 
36
- Rake::TestTask.new(:bench) do |t|
36
+ task :test => :minitest do |t|
37
+ exec 'mspec --format spec core/file/basename core/file/extname core/file/dirname library/pathname'
38
+ end
39
+
40
+ Rake::TestTask.new(bench: :build_lib) do |t|
37
41
  t.libs = %w(lib test)
38
42
  t.pattern = 'test/**/*_benchmark.rb'
39
43
  end
40
44
 
41
- task :default => :test
45
+ Rake::TestTask.new(pbench: :build_lib) do |t|
46
+ t.libs = %w(lib test test/pbench)
47
+ t.pattern = 'test/pbench/pbench_suite.rb'
48
+ end
49
+
50
+ task default: :test
data/faster_path.gemspec CHANGED
@@ -13,17 +13,26 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = "https://github.com/danielpclark/faster_path"
14
14
  spec.license = "MIT OR Apache-2.0"
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|assets|benches)/}) }
17
- spec.bindir = "exe"
18
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
16
+ spec.files = [
17
+ "Cargo.lock", "Cargo.toml", "Gemfile",
18
+ "MIT-LICENSE.txt", "README.md", "Rakefile", "bin/console", "bin/setup",
19
+ "ext/faster_path/extconf.rb", "faster_path.gemspec"
20
+ ]
21
+ spec.files += Dir['lib/**/*']
22
+ spec.files += Dir['src/**/*']
23
+
19
24
  spec.extensions << "ext/faster_path/extconf.rb"
20
25
  spec.require_paths = ["lib"]
21
26
 
27
+ spec.add_dependency "bundler", "~> 1.12"
28
+ spec.add_dependency "rake", "~> 12.0"
22
29
  spec.add_dependency "ffi", "~> 1.9"
23
- spec.add_development_dependency "bundler", "~> 1.12"
24
30
  spec.add_development_dependency "method_source", "~> 0.8.2"
25
- spec.add_development_dependency "rake", "~> 11.1"
26
- spec.add_development_dependency "minitest", "~> 5.8"
31
+ spec.add_development_dependency "minitest", "~> 5.10"
27
32
  spec.add_development_dependency "minitest-reporters", "~> 1.1"
28
33
  spec.add_development_dependency "color_pound_spec_reporter", "~> 0.0.5"
34
+ unless ENV['TRAVIS']
35
+ spec.add_development_dependency "stop_watch", "~> 0.1.0"
36
+ spec.add_development_dependency "gruff", "~> 0.7.0"
37
+ end
29
38
  end
@@ -1,10 +1,20 @@
1
+ require 'pathname'
2
+
1
3
  module FasterPath
2
4
  def self.sledgehammer_everything!
3
5
  ::File.class_eval do
4
- def basename(pth)
5
- FasterPath.basename(pth)
6
- end
7
- end
6
+ def self.basename(pth, ext = '')
7
+ FasterPath.basename(pth, ext)
8
+ end if ENV['WITH_REGRESSION']
9
+
10
+ def self.extname(pth)
11
+ FasterPath.extname(pth)
12
+ end if ENV['WITH_REGRESSION']
13
+
14
+ def self.dirname(pth)
15
+ FasterPath.dirname(pth)
16
+ end if ENV['WITH_REGRESSION']
17
+ end
8
18
 
9
19
  ::Pathname.class_eval do
10
20
  def absolute?
@@ -24,11 +34,20 @@ module FasterPath
24
34
  FasterPath.relative?(@path)
25
35
  end
26
36
 
27
- def add_trailing_separator(pth)
37
+ def add_trailing_separator(pth)
28
38
  FasterPath.add_trailing_separator(pth)
29
- end
30
- private :add_trailing_separator
39
+ end
40
+ private :add_trailing_separator
41
+
42
+ def has_trailing_separator?(pth)
43
+ FasterPath.has_trailing_separator?(pth)
44
+ end
45
+ private :has_trailing_separator?
46
+
47
+ def entries
48
+ FasterPath.entries(@path)
49
+ end if ENV['WITH_REGRESSION']
31
50
  end
51
+ "CAUTION: Monkey patching effects everything! Be very sure you want this!"
32
52
  end
33
53
  end
34
-
@@ -1,11 +1,21 @@
1
+ require 'pathname'
2
+
1
3
  module FasterPath
2
4
  module RefineFile
3
5
  refine File do
4
- def basename(pth)
5
- FasterPath.basename(pth)
6
+ def self.basename(pth, ext = '')
7
+ FasterPath.basename(pth, ext)
8
+ end
9
+
10
+ def self.extname(pth)
11
+ FasterPath.extname(pth)
12
+ end
13
+
14
+ def self.dirname(pth)
15
+ FasterPath.dirname(pth)
6
16
  end
7
17
  end
8
- end
18
+ end
9
19
 
10
20
  module RefinePathname
11
21
  refine Pathname do
@@ -30,6 +40,14 @@ module FasterPath
30
40
  FasterPath.add_trailing_separator(pth)
31
41
  end
32
42
  private :add_trailing_separator
43
+
44
+ def has_trailing_separator?(pth)
45
+ FasterPath.has_trailing_separator?(pth)
46
+ end
47
+
48
+ def entries
49
+ FasterPath.entries(@path)
50
+ end if ENV['WITH_REGRESSION']
33
51
  end
34
52
  end
35
53
  end
@@ -1,3 +1,3 @@
1
1
  module FasterPath
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.10"
3
3
  end
data/lib/faster_path.rb CHANGED
@@ -3,6 +3,14 @@ require 'pathname'
3
3
  require "ffi"
4
4
 
5
5
  module FasterPath
6
+ def self.rust_arch_bits
7
+ Rust.rust_arch_bits
8
+ end
9
+
10
+ def self.ruby_arch_bits
11
+ 1.size * 8
12
+ end
13
+
6
14
  # Spec to Pathname#absolute?
7
15
  def self.absolute?(pth)
8
16
  Rust.is_absolute(pth)
@@ -18,6 +26,10 @@ module FasterPath
18
26
  Rust.is_relative(pth)
19
27
  end
20
28
 
29
+ def self.dirname(pth)
30
+ Rust.dirname(pth)
31
+ end
32
+
21
33
  # Spec to Pathname#chop_basename
22
34
  # WARNING! Pathname#chop_basename in STDLIB doesn't handle blank strings correctly!
23
35
  # This implementation correctly handles blank strings just as Pathname had intended
@@ -33,12 +45,24 @@ module FasterPath
33
45
 
34
46
  def self.basename(pth, ext="")
35
47
  Rust.basename(pth, ext)
36
- end
48
+ end
37
49
 
38
50
  def self.add_trailing_separator(pth)
39
51
  Rust.add_trailing_separator(pth)
40
52
  end
41
53
 
54
+ def self.has_trailing_separator?(pth)
55
+ Rust.has_trailing_separator(pth)
56
+ end
57
+
58
+ def self.extname(pth)
59
+ Rust.extname(pth)
60
+ end
61
+
62
+ def self.entries(pth)
63
+ Array(Rust.entries(pth))
64
+ end
65
+
42
66
  # EXAMPLE
43
67
  #def self.one_and_two
44
68
  # Rust.one_and_two.to_a
@@ -61,6 +85,7 @@ module FasterPath
61
85
  end
62
86
  end
63
87
 
88
+ attach_function :rust_arch_bits, [], :int32
64
89
  attach_function :is_absolute, [ :string ], :bool
65
90
  attach_function :is_directory, [ :string ], :bool
66
91
  attach_function :is_relative, [ :string ], :bool
@@ -71,6 +96,9 @@ module FasterPath
71
96
  attach_function :basename_for_chop, [ :string ], :string # decoupling behavior
72
97
  attach_function :dirname_for_chop, [ :string ], :string # decoupling behavior
73
98
  attach_function :add_trailing_separator, [ :string ], :string
99
+ attach_function :has_trailing_separator, [ :string ], :bool
100
+ attach_function :extname, [ :string ], :string
101
+ attach_function :entries, [ :string ], FromRustArray.by_value
74
102
 
75
103
  # EXAMPLE
76
104
  #attach_function :one_and_two, [], FromRustArray.by_value
data/src/basename.rs CHANGED
@@ -1,86 +1,26 @@
1
- use std::path::MAIN_SEPARATOR;
2
1
  use libc::c_char;
3
- use std::ffi::{CStr,CString};
4
- use std::str;
2
+ use std::ffi::{CStr, CString};
3
+ use path_parsing::extract_last_path_segment;
5
4
 
6
- #[allow(dead_code)]
7
- fn rubyish_basename(string: &str, globish_string: &str) -> String {
8
- let result = if globish_string == ".*" {
9
- let base = string.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("");
10
- let index = base.rfind(".");
11
- let (first, _) = base.split_at(index.unwrap());
12
- first
13
- } else {
14
- if &string[string.len()-globish_string.len()..] == globish_string {
15
- &string[0..string.len()-globish_string.len()]
16
- } else {
17
- string
18
- }.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("")
19
- };
20
- result.to_string()
21
- }
5
+ #[no_mangle]
6
+ pub extern "C" fn basename(c_pth: *const c_char, c_ext: *const c_char) -> *const c_char {
7
+ // TODO: rb_raise on type or encoding errors
8
+ // TODO: support objects that respond to `to_path`
9
+ if c_pth.is_null() || c_ext.is_null() {
10
+ return c_pth;
11
+ }
12
+ let pth = unsafe { CStr::from_ptr(c_pth) }.to_str().unwrap();
13
+ let ext = unsafe { CStr::from_ptr(c_ext) }.to_str().unwrap();
22
14
 
23
- #[test]
24
- fn it_chomps_strings_correctly(){
25
- assert_eq!(rubyish_basename("","") , "");
26
- assert_eq!(rubyish_basename("ruby","") , "ruby");
27
- assert_eq!(rubyish_basename("ruby.rb",".rb") , "ruby");
28
- assert_eq!(rubyish_basename("ruby.rb",".*") , "ruby");
29
- assert_eq!(rubyish_basename(".ruby/ruby.rb",".rb") , "ruby");
30
- assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".rb") , "ruby.rb.swp");
31
- assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".swp") , "ruby.rb");
32
- assert_eq!(rubyish_basename(".ruby/ruby.rb.swp",".*") , "ruby.rb");
33
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp","") , "asdf.rb.swp");
34
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*") , "asdf.rb");
35
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", "*") , "asdf.rb.swp");
36
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".") , "asdf.rb.swp");
37
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*") , "asdf.rb");
38
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb") , "asdf.rb.swp");
39
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".swp") , "asdf.rb");
40
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".sw") , "asdf.rb.swp");
41
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".sw*") , "asdf.rb.swp");
42
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.s*") , "asdf.rb.swp");
43
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".s*") , "asdf.rb.swp");
44
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".s**") , "asdf.rb.swp");
45
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".**") , "asdf.rb.swp");
46
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*") , "asdf.rb");
47
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".*.*") , "asdf.rb.swp");
48
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.swp") , "asdf");
49
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".rb.s*p") , "asdf.rb.swp");
50
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*.s*p") , "asdf.rb.swp");
51
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*.sw*p") , "asdf.rb.swp");
52
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", ".r*b.sw*p") , "asdf.rb.swp");
53
- assert_eq!(rubyish_basename("asdf/asdf.rb.swp", "rb.swp") , "asdf.");
54
- }
15
+ let mut name = extract_last_path_segment(pth);
55
16
 
56
- #[no_mangle]
57
- pub extern fn basename(str_pth: *const c_char, comp_ext: *const c_char) -> *const c_char {
58
- let c_str1 = unsafe {
59
- if str_pth.is_null(){
60
- return str_pth;
61
- }
62
- CStr::from_ptr(str_pth)
63
- };
64
- let c_str2 = unsafe {
65
- if comp_ext.is_null() {
66
- return str_pth;
17
+ if ext == ".*" {
18
+ if let Some(dot_i) = name.rfind('.') {
19
+ name = &name[0..dot_i];
67
20
  }
68
- CStr::from_ptr(comp_ext)
21
+ } else if name.ends_with(ext) {
22
+ name = &name[..name.len() - ext.len()];
69
23
  };
70
- let string = str::from_utf8(c_str1.to_bytes()).unwrap();
71
- let globish_string = str::from_utf8(c_str2.to_bytes()).unwrap();
72
24
 
73
- let result = if globish_string == ".*" {
74
- let base = string.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("");
75
- let index = base.rfind(".");
76
- let (first, _) = base.split_at(index.unwrap());
77
- first
78
- } else {
79
- if &string[string.len()-globish_string.len()..] == globish_string {
80
- &string[0..string.len()-globish_string.len()]
81
- } else {
82
- string
83
- }.rsplit_terminator(MAIN_SEPARATOR).nth(0).unwrap_or("")
84
- };
85
- CString::new(result).unwrap().into_raw()
25
+ CString::new(name).unwrap().into_raw()
86
26
  }
data/src/dirname.rs CHANGED
@@ -1,30 +1,32 @@
1
- use std::path::{Path,MAIN_SEPARATOR};
1
+ use std::path::MAIN_SEPARATOR;
2
2
  use libc::c_char;
3
- use std::ffi::{CStr,CString};
4
- use std::str;
3
+ use std::ffi::{CStr, CString};
4
+ use path_parsing::{last_sep_i, last_non_sep_i, last_non_sep_i_before};
5
5
 
6
6
  #[no_mangle]
7
- pub extern fn dirname(string: *const c_char) -> *const c_char {
8
- let c_str = unsafe {
9
- assert!(!string.is_null());
10
-
11
- CStr::from_ptr(string)
12
- };
13
-
14
- let r_str = str::from_utf8(c_str.to_bytes()).unwrap();
15
-
7
+ pub extern "C" fn dirname(path: *const c_char) -> *const c_char {
8
+ if path.is_null() {
9
+ return path
10
+ }
11
+ let r_str = unsafe { CStr::from_ptr(path) }.to_str().unwrap();
16
12
  if r_str.is_empty() {
17
- return string
13
+ return CString::new(".").unwrap().into_raw();
18
14
  }
19
-
20
- let path = Path::new(r_str).parent().unwrap_or(Path::new(""));
21
-
22
- let out_str = if !path.to_str().unwrap().is_empty() {
23
- format!("{}{}", path.to_str().unwrap(), MAIN_SEPARATOR)
15
+ let non_sep_i = last_non_sep_i(r_str);
16
+ if non_sep_i == -1 {
17
+ return CString::new(MAIN_SEPARATOR.to_string()).unwrap().into_raw();
18
+ }
19
+ let sep_i = last_sep_i(r_str, non_sep_i);
20
+ if sep_i == -1 {
21
+ return CString::new(".").unwrap().into_raw();
22
+ }
23
+ if sep_i == 0 {
24
+ return CString::new(MAIN_SEPARATOR.to_string()).unwrap().into_raw();
25
+ }
26
+ let non_sep_i2 = last_non_sep_i_before(r_str, sep_i);
27
+ if non_sep_i2 != -1 {
28
+ return CString::new(&r_str[..(non_sep_i2 + 1) as usize]).unwrap().into_raw();
24
29
  } else {
25
- format!("{}", path.to_str().unwrap())
26
- };
27
-
28
- let output = CString::new(out_str).unwrap();
29
- output.into_raw()
30
+ return CString::new(MAIN_SEPARATOR.to_string()).unwrap().into_raw();
31
+ }
30
32
  }
data/src/entries.rs ADDED
@@ -0,0 +1,30 @@
1
+ use libc::c_char;
2
+ use std::ffi::{CStr,CString};
3
+ use std::str;
4
+ use std::fs;
5
+ use ruby_array::RubyArray;
6
+
7
+ #[no_mangle]
8
+ pub extern fn entries(string: *const c_char) -> RubyArray {
9
+ let c_str = unsafe {
10
+ assert!(!string.is_null());
11
+
12
+ CStr::from_ptr(string)
13
+ };
14
+
15
+ let r_str = str::from_utf8(c_str.to_bytes()).unwrap();
16
+
17
+ let files = fs::read_dir(r_str).unwrap();
18
+ let mut files_vec = vec![];
19
+
20
+ files_vec.push(CString::new(".").unwrap().into_raw());
21
+ files_vec.push(CString::new("..").unwrap().into_raw());
22
+
23
+ for file in files {
24
+ let file_name_str = file.unwrap().file_name().into_string().unwrap();
25
+ let file_name_cstr = CString::new(file_name_str).unwrap().into_raw();
26
+ files_vec.push(file_name_cstr);
27
+ }
28
+
29
+ RubyArray::from_vec(files_vec)
30
+ }
data/src/extname.rs ADDED
@@ -0,0 +1,20 @@
1
+ use libc::c_char;
2
+ use std::ffi::{CStr, CString};
3
+ use path_parsing::extract_last_path_segment;
4
+
5
+ #[no_mangle]
6
+ pub extern "C" fn extname(c_pth: *const c_char) -> *const c_char {
7
+ if c_pth.is_null() {
8
+ return c_pth
9
+ }
10
+
11
+ let name = extract_last_path_segment(unsafe { CStr::from_ptr(c_pth) }.to_str().unwrap());
12
+
13
+ if let Some(dot_i) = name.rfind('.') {
14
+ if dot_i > 0 && dot_i < name.len() - 1 && name[..dot_i].chars().rev().next().unwrap() != '.' {
15
+ return CString::new(&name[dot_i..]).unwrap().into_raw()
16
+ }
17
+ }
18
+
19
+ CString::new("").unwrap().into_raw()
20
+ }
@@ -0,0 +1,27 @@
1
+ use std::path::{Path, MAIN_SEPARATOR};
2
+ use libc::c_char;
3
+ use std::ffi::CStr;
4
+ use std::str;
5
+
6
+ #[no_mangle]
7
+ pub extern "C" fn has_trailing_separator(string: *const c_char) -> bool {
8
+ let c_str = unsafe {
9
+ if string.is_null() {
10
+ return false
11
+ }
12
+ CStr::from_ptr(string)
13
+ };
14
+
15
+ let r_str = str::from_utf8(c_str.to_bytes()).unwrap_or("");
16
+ let path = Path::new(r_str);
17
+ let last_component = path.iter().last();
18
+ if last_component.is_none() {
19
+ false
20
+ } else {
21
+ let mut parts: Vec<&str> = r_str.rsplit_terminator(MAIN_SEPARATOR).collect();
22
+ parts.retain(|x| !x.is_empty());
23
+ let last_part = parts.first().unwrap_or(&"").chars().last().unwrap_or(MAIN_SEPARATOR);
24
+ let last_char = r_str.chars().last().unwrap();
25
+ (last_part != last_char) && (last_char == MAIN_SEPARATOR)
26
+ }
27
+ }
data/src/is_absolute.rs CHANGED
@@ -1,19 +1,17 @@
1
1
  use libc::c_char;
2
2
  use std::ffi::{CStr};
3
- use std::str;
4
3
  use std::path::MAIN_SEPARATOR;
5
4
 
6
5
 
7
6
  #[no_mangle]
8
- pub extern fn is_absolute(string: *const c_char) -> bool {
9
- let c_str = unsafe {
10
- if string.is_null() {
11
- return false;
12
- }
13
- CStr::from_ptr(string)
14
- };
7
+ pub extern fn is_absolute(path: *const c_char) -> bool {
8
+ if path.is_null() {
9
+ return false;
10
+ }
11
+ let r_str = unsafe { CStr::from_ptr(path) }.to_str().unwrap();
15
12
 
16
- let r_str = str::from_utf8(c_str.to_bytes()).unwrap_or("");
17
-
18
- r_str.chars().next().unwrap_or("muffins".chars().next().unwrap()) == MAIN_SEPARATOR
13
+ match r_str.chars().next() {
14
+ Some(c) => c == MAIN_SEPARATOR,
15
+ None => false
16
+ }
19
17
  }
data/src/is_relative.rs CHANGED
@@ -1,18 +1,16 @@
1
1
  use std::path::MAIN_SEPARATOR;
2
2
  use libc::c_char;
3
3
  use std::ffi::{CStr};
4
- use std::str;
5
4
 
6
5
  #[no_mangle]
7
- pub extern fn is_relative(string: *const c_char) -> bool {
8
- let c_str = unsafe {
9
- if string.is_null() {
10
- return false;
11
- }
12
- CStr::from_ptr(string)
13
- };
6
+ pub extern fn is_relative(path: *const c_char) -> bool {
7
+ if path.is_null() {
8
+ return false;
9
+ }
10
+ let r_str = unsafe { CStr::from_ptr(path) }.to_str().unwrap();
14
11
 
15
- let r_str = str::from_utf8(c_str.to_bytes()).unwrap_or("");
16
-
17
- r_str.chars().next().unwrap_or("muffins".chars().next().unwrap()) != MAIN_SEPARATOR
12
+ match r_str.chars().next() {
13
+ Some(c) => c != MAIN_SEPARATOR,
14
+ None => true
15
+ }
18
16
  }
data/src/lib.rs CHANGED
@@ -18,6 +18,11 @@ pub mod dirname;
18
18
  pub mod basename_for_chop;
19
19
  pub mod dirname_for_chop;
20
20
  pub mod add_trailing_separator;
21
+ pub mod has_trailing_separator;
22
+ pub mod extname;
23
+ pub mod entries;
24
+ pub mod rust_arch_bits;
25
+ mod path_parsing;
21
26
 
22
27
  // EXAMPLE
23
28
  //
@@ -0,0 +1,55 @@
1
+ use std::path::MAIN_SEPARATOR;
2
+
3
+ static SEP: u8 = MAIN_SEPARATOR as u8;
4
+
5
+ pub fn extract_last_path_segment(path: &str) -> &str {
6
+ // Works with bytes directly because MAIN_SEPARATOR is always in the ASCII 7-bit range so we can
7
+ // avoid the overhead of full UTF-8 processing.
8
+ // See src/benches/path_parsing.rs for benchmarks of different approaches.
9
+ let ptr = path.as_ptr();
10
+ let mut i = path.len() as isize - 1;
11
+ while i >= 0 {
12
+ let c = unsafe { *ptr.offset(i) };
13
+ if c != SEP { break; };
14
+ i -= 1;
15
+ }
16
+ let end = (i + 1) as usize;
17
+ while i >= 0 {
18
+ let c = unsafe { *ptr.offset(i) };
19
+ if c == SEP {
20
+ return &path[(i + 1) as usize..end];
21
+ };
22
+ i -= 1;
23
+ }
24
+ &path[..end]
25
+ }
26
+
27
+
28
+ // Returns the byte offset of the last byte preceding a MAIN_SEPARATOR.
29
+ pub fn last_non_sep_i(path: &str) -> isize {
30
+ last_non_sep_i_before(path, path.len() as isize - 1)
31
+ }
32
+
33
+ // Returns the byte offset of the last byte preceding a MAIN_SEPARATOR before the given end offset.
34
+ pub fn last_non_sep_i_before(path: &str, end: isize) -> isize {
35
+ let ptr = path.as_ptr();
36
+ let mut i = end;
37
+ while i >= 0 {
38
+ if unsafe { *ptr.offset(i) } != SEP { break; };
39
+ i -= 1;
40
+ }
41
+ i
42
+ }
43
+
44
+ // Returns the byte offset of the last MAIN_SEPARATOR before the given end offset.
45
+ pub fn last_sep_i(path: &str, end: isize) -> isize {
46
+ let ptr = path.as_ptr();
47
+ let mut i = end - 1;
48
+ while i >= 0 {
49
+ if unsafe { *ptr.offset(i) } == SEP {
50
+ return i;
51
+ };
52
+ i -= 1;
53
+ }
54
+ -1
55
+ }
data/src/ruby_array.rs CHANGED
@@ -9,10 +9,10 @@ pub struct RubyArray {
9
9
 
10
10
  impl RubyArray {
11
11
  #[allow(dead_code)]
12
- fn from_vec<T>(vec: Vec<T>) -> RubyArray {
13
- let array = RubyArray {
14
- data: vec.as_ptr() as *const libc::c_void,
15
- len: vec.len() as libc::size_t
12
+ pub fn from_vec<T>(vec: Vec<T>) -> RubyArray {
13
+ let array = RubyArray {
14
+ data: vec.as_ptr() as *const libc::c_void,
15
+ len: vec.len() as libc::size_t
16
16
  };
17
17
  mem::forget(vec);
18
18
  array
@@ -0,0 +1,11 @@
1
+ #[no_mangle]
2
+ pub extern fn rust_arch_bits() -> i32 {
3
+ use std::mem::size_of;
4
+ let s = size_of::<usize>() * 8;
5
+ s as i32
6
+ }
7
+
8
+ #[test]
9
+ fn it_is_32_or_64(){
10
+ assert!(rust_arch_bits() == 32 || rust_arch_bits() == 64);
11
+ }
metadata CHANGED
@@ -1,85 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faster_path
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel P. Clark
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-20 00:00:00.000000000 Z
11
+ date: 2017-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: ffi
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.9'
19
+ version: '1.12'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.9'
26
+ version: '1.12'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.12'
34
- type: :development
33
+ version: '12.0'
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.12'
40
+ version: '12.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: method_source
42
+ name: ffi
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.8.2
48
- type: :development
47
+ version: '1.9'
48
+ type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.8.2
54
+ version: '1.9'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rake
56
+ name: method_source
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '11.1'
61
+ version: 0.8.2
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '11.1'
68
+ version: 0.8.2
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: minitest
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '5.8'
75
+ version: '5.10'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '5.8'
82
+ version: '5.10'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: minitest-reporters
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,34 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: 0.0.5
111
+ - !ruby/object:Gem::Dependency
112
+ name: stop_watch
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.1.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.1.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: gruff
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 0.7.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 0.7.0
111
139
  description: FasterPath is a reimplementation of Pathname for better performance.
112
140
  email:
113
141
  - 6ftdan@gmail.com
@@ -116,8 +144,6 @@ extensions:
116
144
  - ext/faster_path/extconf.rb
117
145
  extra_rdoc_files: []
118
146
  files:
119
- - ".gitignore"
120
- - ".travis.yml"
121
147
  - Cargo.lock
122
148
  - Cargo.toml
123
149
  - Gemfile
@@ -138,13 +164,18 @@ files:
138
164
  - src/both_are_blank.rs
139
165
  - src/dirname.rs
140
166
  - src/dirname_for_chop.rs
167
+ - src/entries.rs
168
+ - src/extname.rs
169
+ - src/has_trailing_separator.rs
141
170
  - src/is_absolute.rs
142
171
  - src/is_blank.rs
143
172
  - src/is_directory.rs
144
173
  - src/is_relative.rs
145
174
  - src/lib.rs
175
+ - src/path_parsing.rs
146
176
  - src/ruby_array.rs
147
177
  - src/ruby_string.rs
178
+ - src/rust_arch_bits.rs
148
179
  homepage: https://github.com/danielpclark/faster_path
149
180
  licenses:
150
181
  - MIT OR Apache-2.0
@@ -165,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
196
  version: '0'
166
197
  requirements: []
167
198
  rubyforge_project:
168
- rubygems_version: 2.6.4
199
+ rubygems_version: 2.6.10
169
200
  signing_key:
170
201
  specification_version: 4
171
202
  summary: Reimplementation of Pathname for better performance
data/.gitignore DELETED
@@ -1,13 +0,0 @@
1
- /.bundle/
2
- /target/
3
- /.yardoc
4
- /Gemfile.lock
5
- /_yardoc/
6
- /coverage/
7
- /doc/
8
- /pkg/
9
- /spec/reports/
10
- /tmp/
11
- mkmf.log
12
- **/*.swp
13
- **/*.gem
data/.travis.yml DELETED
@@ -1,13 +0,0 @@
1
- sudo: true
2
- language: ruby
3
- rvm:
4
- - 2.2.5
5
- - 2.3.1
6
- env:
7
- - TEST_MONKEYPATCHES=true
8
- - TEST_MONKEYPATCHES=false
9
- before_install:
10
- - gem install bundler -v 1.12.5
11
- - curl -sSf https://static.rust-lang.org/rustup.sh | sudo sh -s -- --channel=nightly
12
- - bundle && bundle exec rake build_src && rake clean_src
13
- script: bundle exec rake test