faster_path 0.3.8 → 0.3.9

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
  SHA256:
3
- metadata.gz: 71e809651aa4aa3df08645c40f0d616e3f061648ddeeb0e29086e578003df395
4
- data.tar.gz: ded1645695ee0ddfd84261e25be66ed6f96b5386f45e093ecb46c0a6301f8896
3
+ metadata.gz: 3a5d299440e18a99c763e17b085b7962bfca0f2555b0adf8eb053c77b2d8d282
4
+ data.tar.gz: fee3ac374b164a25d1af43e0e8cd36ea68f57421db38637b7275a504e38bb391
5
5
  SHA512:
6
- metadata.gz: ad20f8a279a81ccff6ce068f7c778649d3f71b59576d625e82e4bd1713c78d44dd10eb859b96b925f14c44f64171449c2fd17b46710a53a8e238f75ec17b5195
7
- data.tar.gz: 0f2135684d07b33503b4ef62b4f6a750b5648d3b450d8ad0a55f9bdd54853355b86ba9e3e2fd546eb24a9148d9eace955b3642ad9bfeba4ca55c52e8de262bce
6
+ metadata.gz: 79d7b3931800982e78cc7f00c2fd2281932a2ac3775784f5f9d5c59bf974cd41f30d2d03c6216ce210fdc1754659b714cda6c19cca84e39a464e2bb62f7afa9c
7
+ data.tar.gz: 1a5b56dff703996bdcf60838ad9881528e51867aa2e9c5d996ff9c28996fb1297e7dacc9380a42def9ff9358e87ac4576a2f7536d5c73b685332b73b9e687122
data/Cargo.lock CHANGED
@@ -1,13 +1,13 @@
1
1
  [[package]]
2
2
  name = "array_tool"
3
- version = "1.0.1"
3
+ version = "1.0.3"
4
4
  source = "registry+https://github.com/rust-lang/crates.io-index"
5
5
 
6
6
  [[package]]
7
7
  name = "faster_path"
8
8
  version = "0.0.1"
9
9
  dependencies = [
10
- "array_tool 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
10
+ "array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
11
11
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
12
12
  "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
13
13
  "ruby-sys 0.3.0 (git+https://github.com/danielpclark/ruby-sys?branch=playground)",
@@ -26,7 +26,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
26
26
 
27
27
  [[package]]
28
28
  name = "libc"
29
- version = "0.2.37"
29
+ version = "0.2.39"
30
30
  source = "registry+https://github.com/rust-lang/crates.io-index"
31
31
 
32
32
  [[package]]
@@ -34,31 +34,31 @@ name = "memchr"
34
34
  version = "2.0.1"
35
35
  source = "registry+https://github.com/rust-lang/crates.io-index"
36
36
  dependencies = [
37
- "libc 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
37
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
38
38
  ]
39
39
 
40
40
  [[package]]
41
41
  name = "ruby-sys"
42
42
  version = "0.3.0"
43
- source = "git+https://github.com/danielpclark/ruby-sys?branch=playground#184a3cd3bd39b525ff956f7cba1770bbf1522d8b"
43
+ source = "git+https://github.com/danielpclark/ruby-sys?branch=playground#a3d47da5f9d1e597c62a8f03fb7d3a3596393c47"
44
44
  dependencies = [
45
- "libc 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
45
+ "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)",
46
46
  ]
47
47
 
48
48
  [[package]]
49
49
  name = "ruru"
50
50
  version = "0.9.3"
51
- source = "git+https://github.com/danielpclark/ruru?branch=playground#c9b59118ebcf034874d491a0c1f3b57ef05c183a"
51
+ source = "git+https://github.com/danielpclark/ruru?branch=playground#ab84ad4b97c41bfe6fd6d0f8540b2efd77583561"
52
52
  dependencies = [
53
53
  "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
54
54
  "ruby-sys 0.3.0 (git+https://github.com/danielpclark/ruby-sys?branch=playground)",
55
55
  ]
56
56
 
57
57
  [metadata]
58
- "checksum array_tool 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7894b81e06d8a2376f015b34dd89770c549932ff90bba6427f7cd83243bb629b"
58
+ "checksum array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271"
59
59
  "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
60
60
  "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
61
- "checksum libc 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)" = "56aebce561378d99a0bb578f8cb15b6114d2a1814a6c7949bbe646d968bb4fa9"
61
+ "checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
62
62
  "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
63
63
  "checksum ruby-sys 0.3.0 (git+https://github.com/danielpclark/ruby-sys?branch=playground)" = "<none>"
64
64
  "checksum ruru 0.9.3 (git+https://github.com/danielpclark/ruru?branch=playground)" = "<none>"
data/README.md CHANGED
@@ -140,23 +140,23 @@ Current methods implemented:
140
140
 
141
141
  |FasterPath Rust Implementation|Ruby 2.5.0 Implementation|Time Shaved Off|
142
142
  |---|---|:---:|
143
- | `FasterPath.absolute?` | `Pathname#absolute?` | 95.8% |
144
- | `FasterPath.add_trailing_separator` | `Pathname#add_trailing_separator` | 68.7% |
145
- | `FasterPath.basename` | `File.basename` | 49.4% |
146
- | `FasterPath.children` | `Pathname#children` | 39.2% |
147
- | `FasterPath.chop_basename` | `Pathname#chop_basename` | 76.6% |
148
- | `FasterPath.cleanpath_aggressive` | `Pathname#cleanpath_aggressive` | 78.2% |
149
- | `FasterPath.cleanpath_conservative` | `Pathname#cleanpath_conservative` | 74.1% |
150
- | `FasterPath.del_trailing_separator` | `Pathname#del_trailing_separator` | 87.7% |
151
- | `FasterPath.directory?` | `Pathname#directory?` | 16.8% |
152
- | `FasterPath.dirname` | `File.dirname` | 28.9% |
153
- | `FasterPath.entries` | `Pathname#entries` | 36.1% |
154
- | `FasterPath.extname` | `File.extname` | 66.2% |
155
- | `FasterPath.has_trailing_separator?` | `Pathname#has_trailing_separator` | 86.3% |
156
- | `FasterPath.plus` | `Pathname#join` | 67.9% |
157
- | `FasterPath.plus` | `Pathname#plus` | 86.6% |
158
- | `FasterPath.relative?` | `Pathname#relative?` | 87.2% |
159
- | `FasterPath.relative_path_from` | `Pathname#relative_path_from` | 74.0% |
143
+ | `FasterPath.absolute?` | `Pathname#absolute?` | 95.3% |
144
+ | `FasterPath.add_trailing_separator` | `Pathname#add_trailing_separator` | 48.4% |
145
+ | `FasterPath.basename` | `File.basename` | 12.0% |
146
+ | `FasterPath.children` | `Pathname#children` | 34.4% |
147
+ | `FasterPath.chop_basename` | `Pathname#chop_basename` | 83.4% |
148
+ | `FasterPath.cleanpath_aggressive` | `Pathname#cleanpath_aggressive` | 94.1% |
149
+ | `FasterPath.cleanpath_conservative` | `Pathname#cleanpath_conservative` | 93.5% |
150
+ | `FasterPath.del_trailing_separator` | `Pathname#del_trailing_separator` | 85.4% |
151
+ | `FasterPath.directory?` | `Pathname#directory?` | 6.4% |
152
+ | `FasterPath.dirname` | `File.dirname` | 55.4% |
153
+ | `FasterPath.entries` | `Pathname#entries` | 41.0% |
154
+ | `FasterPath.extname` | `File.extname` | 63.1% |
155
+ | `FasterPath.has_trailing_separator?` | `Pathname#has_trailing_separator` | 88.9% |
156
+ | `FasterPath.plus` | `Pathname#join` | 72.0% |
157
+ | `FasterPath.plus` | `Pathname#plus` | 87.7% |
158
+ | `FasterPath.relative?` | `Pathname#relative?` | 92.6% |
159
+ | `FasterPath.relative_path_from` | `Pathname#relative_path_from` | 93.3% |
160
160
 
161
161
  You may choose to use the methods directly, or scope change to rewrite behavior on the
162
162
  standard library with the included refinements, or even call a method to monkeypatch
data/Rakefile CHANGED
@@ -5,24 +5,21 @@ require 'faster_path/thermite_initialize'
5
5
 
6
6
  desc 'System Details'
7
7
  task :sysinfo do
8
- puts "faster_path #{FasterPath::VERSION}"
9
- puts
8
+ puts "** FasterPath - faster_path v#{FasterPath::VERSION} **"
9
+ puts RUBY_DESCRIPTION
10
10
  puts `rustc -Vv`
11
+ puts `ldd --version`.scan(/(.*)\n/).first if RbConfig::CONFIG["host_os"].to_s[/linux/]
11
12
  puts `cargo -Vv`
12
- IO.read('Cargo.toml').split('[dependencies]').last.split("\n").
13
- select {|line| line =~ /=/ }.
14
- each {|line| puts "%s\t%s" % line.match(/(\S+)[ ="']{1,4}([\d.]+)["']?/).captures }
15
- puts
16
- puts RUBY_DESCRIPTION
17
- puts "bundler\t#{Bundler::VERSION}"
18
- puts "rake\t#{Rake::VERSION}"
19
- require 'ffi/version';
20
- puts "ffi\t#{FFI::VERSION}"
21
- begin
22
- puts "%s\t%s" % IO.read('Gemfile.lock').match(/(mspec) \(([\d\.]+)\)/).captures
23
- rescue Errno::ENOENT => _
24
- puts "\nNo Gemfile.lock"
25
- end
13
+ deps = Regexp.new(/name = "([\w\-]+)"\nversion = "(\d+\.\d+\.\d+)"/)
14
+ puts "** Rust dependencies **"
15
+ IO.read("Cargo.lock").
16
+ scan(deps).
17
+ delete_if {|i| i[0] == "faster_path" }.
18
+ each {|name, version| puts "#{name.ljust(20)}#{version}"}
19
+ puts "** Ruby dependencies **"
20
+ deps = IO.read("Gemfile.lock")
21
+ puts deps[(deps =~ /DEPENDENCIES/)..-1].sub("\n\n", "\n")
22
+ puts "RAKE\n #{Rake::VERSION}"
26
23
  end
27
24
 
28
25
  thermite = Thermite::Tasks.new
@@ -34,36 +31,6 @@ task :contrib do
34
31
  ' > Contributors.md;git shortlog -s -n -e | sed 's/^......./- /' >> Contributors.md"
35
32
  end
36
33
 
37
- desc "Add libruby to deps"
38
- task :libruby_release do
39
- filename = RbConfig::CONFIG["LIBRUBY_ALIASES"].split(" ").first
40
- libfile = File.join(RbConfig::CONFIG["libdir"], filename)
41
- deps = "target/release/deps"
42
-
43
- printf "Copying libruby.so ... "
44
- unless File.exist? "#{deps}/#{filename}"
45
- FileUtils.mkdir_p deps
46
- FileUtils.cp libfile, deps
47
- end
48
- exit 1 unless File.exist? "#{deps}/#{filename}"
49
- puts "libruby.so copied."
50
- end
51
-
52
- desc "Add libruby to debug deps"
53
- task :libruby_debug do
54
- filename = RbConfig::CONFIG["LIBRUBY_ALIASES"].split(" ").first
55
- libfile = File.join(RbConfig::CONFIG["libdir"], filename)
56
- deps = "target/debug/deps"
57
-
58
- printf "Copying libruby.so ... "
59
- unless File.exist? "#{deps}/#{filename}"
60
- FileUtils.mkdir_p deps
61
- FileUtils.cp libfile, deps
62
- end
63
- exit 1 unless File.exist? "#{deps}/#{filename}"
64
- puts "libruby.so copied."
65
- end
66
-
67
34
  desc 'Build + clean up Rust extension'
68
35
  task build_lib: 'thermite:build' do
69
36
  thermite.run_cargo 'clean'
@@ -78,7 +45,7 @@ task :lint do
78
45
  end
79
46
 
80
47
  desc "Run Rust Tests"
81
- task cargo: :libruby_debug do
48
+ task :cargo do
82
49
  sh "cargo test -- --nocapture"
83
50
  end
84
51
 
@@ -117,6 +84,9 @@ Rake::TestTask.new(bench: :build_lib) do |t|
117
84
  end
118
85
 
119
86
  Rake::TestTask.new(pbench: :build_lib) do |t|
87
+ if ARGV.last == '--long-run'
88
+ ENV['LONG_RUN'] = '10'
89
+ end
120
90
  t.libs = %w[lib test test/pbench]
121
91
  t.pattern = 'test/pbench/pbench_suite.rb'
122
92
  end
@@ -5,6 +5,9 @@ require 'faster_path/thermite_initialize'
5
5
  require 'fiddle'
6
6
  require 'fiddle/import'
7
7
 
8
+ # FasterPath module behaves as a singleton object with the alternative method
9
+ # implementations for Pathname, and some for File, available directly on it.
10
+ #
8
11
  module FasterPath
9
12
  FFI_LIBRARY = begin
10
13
  toplevel_dir = File.dirname(__dir__)
@@ -23,30 +26,50 @@ module FasterPath
23
26
  private_class_method :children_compat
24
27
  end
25
28
 
29
+ # The Rust architecture bit width: 64bits or 32bits
30
+ # @return [Integer]
26
31
  def self.rust_arch_bits
27
32
  Rust.rust_arch_bits
28
33
  end
29
34
 
35
+ # The Ruby architecture bit width: 64bits or 32bits
36
+ # @return [Integer]
30
37
  def self.ruby_arch_bits
31
38
  1.size * 8
32
39
  end
33
40
 
41
+ # A very fast way to check if a string is blank
42
+ # @param str [#to_s]
43
+ # @return [true, false]
34
44
  def self.blank?(str)
35
45
  "#{str}".strip.empty?
36
46
  end
37
47
 
48
+ # Rust implementation of File.basename
49
+ # @param pth [String] the path to evaluate
50
+ # @param ext [String] any extension to remove
51
+ # @return [String]
38
52
  def self.basename(pth, ext="")
39
53
  Public.send(:basename, pth, ext)
40
54
  end
41
55
 
56
+ # Rust implementation of Pathname.children
57
+ # @param pth [String] the path to evaluate
58
+ # @param with_directory [true, false] whether to include directory in results
59
+ # @return [Array<String>]
42
60
  def self.children(pth, with_directory=true)
43
61
  Public.send(:children, pth, with_directory)
44
62
  end
45
63
 
64
+ # Rust implementation of Pathname.children
65
+ # @param pth [String] the path to evaluate
66
+ # @param with_directory [true, false] whether to include directory in results
67
+ # @return [Array<Pathname>]
46
68
  def self.children_compat(pth, with_directory=true)
47
69
  Public.send(:children_compat, pth, with_directory)
48
70
  end
49
71
 
72
+ # @private
50
73
  module Rust
51
74
  extend Fiddle::Importer
52
75
  dlload FFI_LIBRARY
@@ -1,23 +1,29 @@
1
1
  require 'pathname'
2
2
 
3
+ # :nodoc:
3
4
  module FasterPath
5
+ # Core module for applying monkeypatches to `Pathname` and `File`
4
6
  module MonkeyPatches
7
+ # Monkeypatch just `File` specific methods
5
8
  # rubocop:disable Metrics/CyclomaticComplexity
6
9
  # rubocop:disable Metrics/PerceivedComplexity
7
10
  def self._ruby_core_file!
8
11
  ::File.class_eval do
12
+ # @private :nodoc:
9
13
  def self.basename(pth, ext = '')
10
14
  pth = pth.to_path if pth.respond_to? :to_path
11
15
  raise TypeError unless pth.is_a?(String) && ext.is_a?(String)
12
16
  FasterPath.basename(pth, ext)
13
17
  end
14
18
 
19
+ # @private :nodoc:
15
20
  def self.extname(pth)
16
21
  pth = pth.to_path if pth.respond_to? :to_path
17
22
  raise TypeError unless pth.is_a? String
18
23
  FasterPath.extname(pth)
19
24
  end
20
25
 
26
+ # @private :nodoc:
21
27
  def self.dirname(pth)
22
28
  pth = pth.to_path if pth.respond_to? :to_path
23
29
  raise TypeError unless pth.is_a? String
@@ -26,68 +32,83 @@ module FasterPath
26
32
  end
27
33
  end
28
34
 
35
+ # Monkeypatch just `Pathname` specific methods
29
36
  # rubocop:disable Metrics/MethodLength
30
37
  def self._ruby_library_pathname!
31
38
  ::Pathname.class_eval do
39
+ # @private :nodoc:
32
40
  def absolute?
33
41
  FasterPath.absolute?(@path)
34
42
  end
35
43
 
44
+ # @private :nodoc:
36
45
  def add_trailing_separator(pth)
37
46
  FasterPath.add_trailing_separator(pth)
38
47
  end
39
48
  private :add_trailing_separator
40
49
 
50
+ # @private :nodoc:
41
51
  def children(with_dir=true)
42
52
  FasterPath.children_compat(@path, with_dir)
43
53
  end if !!ENV['WITH_REGRESSION']
44
54
 
55
+ # @private :nodoc:
45
56
  def chop_basename(pth)
46
57
  FasterPath.chop_basename(pth)
47
58
  end
48
59
  private :chop_basename
49
60
 
61
+ # @private :nodoc:
50
62
  def cleanpath_aggressive
51
63
  Pathname.new(FasterPath.cleanpath_aggressive(@path))
52
64
  end
53
65
  private :cleanpath_aggressive
54
66
 
67
+ # @private :nodoc:
55
68
  def cleanpath_conservative
56
69
  Pathname.new(FasterPath.cleanpath_conservative(@path))
57
70
  end
58
71
  private :cleanpath_conservative
59
72
 
73
+ # @private :nodoc:
60
74
  def del_trailing_separator(pth)
61
75
  FasterPath.del_trailing_separator(pth)
62
76
  end
63
77
  private :del_trailing_separator
64
78
 
79
+ # @private :nodoc:
65
80
  def directory?
66
81
  FasterPath.directory?(@path)
67
82
  end
68
83
 
84
+ # @private :nodoc:
69
85
  def entries
70
86
  FasterPath.entries_compat(@path)
71
87
  end if !!ENV['WITH_REGRESSION']
72
88
 
89
+ # @private :nodoc:
73
90
  def has_trailing_separator?(pth)
74
91
  FasterPath.has_trailing_separator?(pth)
75
92
  end
76
93
  private :has_trailing_separator?
77
94
 
95
+ # @private :nodoc:
78
96
  def join(*args)
79
97
  FasterPath.join(self, *args)
80
98
  end
81
99
 
100
+ # @private :nodoc:
82
101
  def plus(pth, pth2)
83
102
  FasterPath.plus(pth, pth2)
84
103
  end
85
104
  private :plus
86
105
 
106
+ # @private :nodoc:
87
107
  def relative?
88
108
  FasterPath.relative?(@path)
89
109
  end
90
110
 
111
+ # @private :nodoc:
91
112
  def relative_path_from(other)
92
113
  FasterPath.relative_path_from(@path, other)
93
114
  end
@@ -96,9 +117,9 @@ module FasterPath
96
117
  end
97
118
  private_constant :MonkeyPatches
98
119
 
99
- def self.sledgehammer_everything!()
120
+ # Applies all performant monkeypatches to both `Pathname` and `File`
121
+ def self.sledgehammer_everything!
100
122
  MonkeyPatches._ruby_core_file!
101
123
  MonkeyPatches._ruby_library_pathname!
102
- "CAUTION: Monkey patching effects everything! Be very sure you want this!"
103
124
  end
104
125
  end
@@ -1,20 +1,25 @@
1
1
  require 'pathname'
2
2
 
3
+ # @private :nodoc:
3
4
  module FasterPath
5
+ # @private :nodoc:
4
6
  module RefineFile
5
7
  refine File do
8
+ # @private :nodoc:
6
9
  def self.basename(pth, ext = '')
7
10
  pth = pth.to_path if pth.respond_to? :to_path
8
11
  raise TypeError unless pth.is_a?(String) && ext.is_a?(String)
9
12
  FasterPath.basename(pth, ext)
10
13
  end
11
14
 
15
+ # @private :nodoc:
12
16
  def self.extname(pth)
13
17
  pth = pth.to_path if pth.respond_to? :to_path
14
18
  raise TypeError unless pth.is_a? String
15
19
  FasterPath.extname(pth)
16
20
  end
17
21
 
22
+ # @private :nodoc:
18
23
  def self.dirname(pth)
19
24
  pth = pth.to_path if pth.respond_to? :to_path
20
25
  raise TypeError unless pth.is_a? String
@@ -23,66 +28,81 @@ module FasterPath
23
28
  end
24
29
  end
25
30
 
31
+ # @private :nodoc:
26
32
  module RefinePathname
27
33
  refine Pathname do
34
+ # @private :nodoc:
28
35
  def absolute?
29
36
  FasterPath.absolute?(@path)
30
37
  end
31
38
 
39
+ # @private :nodoc:
32
40
  def add_trailing_separator(pth)
33
41
  FasterPath.add_trailing_separator(pth)
34
42
  end
35
43
  private :add_trailing_separator
36
44
 
45
+ # @private :nodoc:
37
46
  def children(with_dir=true)
38
47
  FasterPath.children_compat(@path, with_dir)
39
48
  end if !!ENV['WITH_REGRESSION']
40
49
 
50
+ # @private :nodoc:
41
51
  def chop_basename(pth)
42
52
  FasterPath.chop_basename(pth)
43
53
  end
44
54
  private :chop_basename
45
55
 
56
+ # @private :nodoc:
46
57
  def cleanpath_aggressive
47
58
  Pathname.new(FasterPath.cleanpath_aggressive(@path))
48
59
  end
49
60
  private :cleanpath_aggressive
50
61
 
62
+ # @private :nodoc:
51
63
  def cleanpath_conservative
52
64
  Pathname.new(FasterPath.cleanpath_conservative(@path))
53
65
  end
54
66
  private :cleanpath_conservative
55
67
 
68
+ # @private :nodoc:
56
69
  def del_trailing_separator(pth)
57
70
  FasterPath.del_trailing_separator(pth)
58
71
  end
59
72
  private :del_trailing_separator
60
73
 
74
+ # @private :nodoc:
61
75
  def directory?
62
76
  FasterPath.directory?(@path)
63
77
  end
64
78
 
79
+ # @private :nodoc:
65
80
  def entries
66
81
  FasterPath.entries_compat(@path)
67
82
  end if !!ENV['WITH_REGRESSION']
68
83
 
84
+ # @private :nodoc:
69
85
  def has_trailing_separator?(pth)
70
86
  FasterPath.has_trailing_separator?(pth)
71
87
  end
72
88
 
89
+ # @private :nodoc:
73
90
  def join(*args)
74
91
  FasterPath.join(self, *args)
75
92
  end
76
93
 
94
+ # @private :nodoc:
77
95
  def plus(pth, pth2)
78
96
  FasterPath.plus(pth, pth2)
79
97
  end
80
98
  private :plus
81
99
 
100
+ # @private :nodoc:
82
101
  def relative?
83
102
  FasterPath.relative?(@path)
84
103
  end
85
104
 
105
+ # @private :nodoc:
86
106
  def relative_path_from(other)
87
107
  FasterPath.relative_path_from(@path, other)
88
108
  end
@@ -2,14 +2,19 @@ require 'rubygems'
2
2
  require 'rake/tasklib'
3
3
  require_relative './version'
4
4
 
5
+ # @private :nodoc:
5
6
  module Thermite
7
+ # @private :nodoc:
6
8
  class Config
9
+ # @private :nodoc:
7
10
  def ruby_version
8
11
  "ruby#{RUBY_VERSION}"
9
12
  end
10
13
  end
11
14
 
15
+ # @private :nodoc:
12
16
  class Tasks < Rake::TaskLib
17
+ # @private :nodoc:
13
18
  def github_download_uri(_tag, version)
14
19
  "#{github_uri}/releases/download/v#{FasterPath::VERSION}/#{config.tarball_filename(version)}"
15
20
  end
@@ -1,3 +1,3 @@
1
1
  module FasterPath
2
- VERSION = "0.3.8"
2
+ VERSION = "0.3.9"
3
3
  end
@@ -1,6 +1,8 @@
1
1
  extern crate memchr;
2
2
 
3
- use path_parsing::{find_last_sep_pos, find_last_non_sep_pos, find_last_dot_pos};
3
+ use self::memchr::memrchr;
4
+
5
+ use path_parsing::{find_last_sep_pos, find_last_non_sep_pos};
4
6
 
5
7
  pub fn basename<'a>(path: &'a str, ext: &str) -> &'a str {
6
8
  let bytes: &[u8] = path.as_bytes();
@@ -20,15 +22,87 @@ pub fn basename<'a>(path: &'a str, ext: &str) -> &'a str {
20
22
  left = last_slash_pos + 1;
21
23
  }
22
24
  }
25
+ &path[left..left + ext_end(&bytes[left..right], ext)]
26
+ }
27
+
28
+ fn ext_end(slice: &[u8], ext: &str) -> usize {
29
+ if ext.len() >= slice.len() || slice == b"." || slice == b".." {
30
+ return slice.len();
31
+ }
23
32
  let ext_bytes = ext.as_bytes();
24
- if ext_bytes == b".*" {
25
- if let Some(dot_pos) = find_last_dot_pos(&bytes[left..right]) {
26
- right = left + dot_pos;
27
- }
28
- } else if bytes[left..right].ends_with(ext_bytes) {
29
- right -= ext_bytes.len();
33
+ if ext_bytes.len() == 2 && *ext_bytes.get(1).unwrap() == b'*' {
34
+ match memrchr(*ext_bytes.get(0).unwrap(), slice) {
35
+ Some(end) if end != 0 => return end,
36
+ _ => {}
37
+ };
38
+ } else if slice.ends_with(ext_bytes) {
39
+ return slice.len() - ext_bytes.len();
30
40
  }
31
- &path[left..right]
41
+ slice.len()
42
+ }
43
+
44
+ #[test]
45
+ fn non_dot_asterisk_ext() {
46
+ // This is undocumented Ruby functionality. We match it in case some code out there relies on it.
47
+ assert_eq!(basename("abc", "b*"), "a");
48
+ assert_eq!(basename("abc", "abc"), "abc");
49
+ assert_eq!(basename("abc", "a*"), "abc");
50
+ assert_eq!(basename("playlist", "l*"), "play");
51
+ // Treated as literal "*":
52
+ assert_eq!(basename("playlist", "yl*"), "playlist");
53
+ assert_eq!(basename("playl*", "yl*"), "pla");
54
+ }
55
+
56
+ #[test]
57
+ fn empty() {
58
+ assert_eq!(basename("", ""), "");
59
+ assert_eq!(basename("", ".*"), "");
60
+ assert_eq!(basename("", ".a"), "");
61
+ }
62
+
63
+ #[test]
64
+ fn sep() {
65
+ assert_eq!(basename("/", ""), "/");
66
+ assert_eq!(basename("//", ""), "/");
67
+ }
68
+
69
+ #[test]
70
+ fn trailing_dot() {
71
+ assert_eq!(basename("file.test.", ""), "file.test.");
72
+ assert_eq!(basename("file.test.", "."), "file.test");
73
+ assert_eq!(basename("file.test.", ".*"), "file.test");
74
+ }
75
+
76
+ #[test]
77
+ fn trailing_dot_dot() {
78
+ assert_eq!(basename("a..", ".."), "a");
79
+ assert_eq!(basename("a..", ".*"), "a.");
80
+ }
81
+
82
+ #[test]
83
+ fn dot() {
84
+ assert_eq!(basename(".", ""), ".");
85
+ assert_eq!(basename(".", "."), ".");
86
+ assert_eq!(basename(".", ".*"), ".");
87
+ }
88
+
89
+ #[test]
90
+ fn dot_dot() {
91
+ assert_eq!(basename("..", ""), "..");
92
+ assert_eq!(basename("..", ".*"), "..");
93
+ assert_eq!(basename("..", ".."), "..");
94
+ assert_eq!(basename("..", "..."), "..");
95
+ }
96
+
97
+ #[test]
98
+ fn non_dot_ext() {
99
+ assert_eq!(basename("abc", "bc"), "a");
100
+ }
101
+
102
+ #[test]
103
+ fn basename_eq_ext() {
104
+ assert_eq!(basename(".x", ".x"), ".x");
105
+ assert_eq!(basename(".x", ".*"), ".x");
32
106
  }
33
107
 
34
108
  #[test]
@@ -11,13 +11,16 @@ pub trait TryFrom<T>: Sized {
11
11
  fn try_from(value: T) -> Result<Self, Self::Error>;
12
12
  }
13
13
 
14
+ #[cfg(windows)]
14
15
  #[inline]
15
16
  pub fn is_same_path(a: &str, b: &str) -> bool {
16
- if cfg!(windows) {
17
- a.to_uppercase() == b.to_uppercase()
18
- } else {
19
- a == b
20
- }
17
+ a.to_uppercase() == b.to_uppercase()
18
+ }
19
+
20
+ #[cfg(not(windows))]
21
+ #[inline]
22
+ pub fn is_same_path(a: &str, b: &str) -> bool {
23
+ a == b
21
24
  }
22
25
 
23
26
  pub fn anyobject_to_string(item: AnyObject) -> Result<String, RubyDebugInfo> {
@@ -10,12 +10,6 @@ lazy_static! {
10
10
  pub static ref SEP_STR: &'static str = str::from_utf8(&[SEP]).unwrap();
11
11
  }
12
12
 
13
- // Returns the byte offset of the last byte that equals MAIN_SEPARATOR.
14
- #[inline(always)]
15
- pub fn find_last_dot_pos(bytes: &[u8]) -> Option<usize> {
16
- memrchr(b'.', bytes)
17
- }
18
-
19
13
  // Returns the byte offset of the last byte that equals MAIN_SEPARATOR.
20
14
  #[inline(always)]
21
15
  pub fn find_last_sep_pos(bytes: &[u8]) -> Option<usize> {
@@ -1,11 +1,10 @@
1
+ use std::iter;
1
2
  use helpers::{is_same_path, to_str};
2
3
  use path_parsing::SEP_STR;
3
4
  use cleanpath_aggressive::cleanpath_aggressive;
4
- extern crate array_tool;
5
- use self::array_tool::iter::ZipOpt;
6
- use ruru;
7
- use chop_basename;
5
+ use chop_basename::chop_basename;
8
6
  use pathname::Pathname;
7
+ use ruru;
9
8
  use ruru::{Exception as Exc, AnyException as Exception};
10
9
 
11
10
  type MaybeString = Result<ruru::RString, ruru::result::Error>;
@@ -14,79 +13,58 @@ pub fn relative_path_from(itself: MaybeString, base_directory: MaybeString) -> R
14
13
  let dest_directory = cleanpath_aggressive(to_str(&itself));
15
14
  let base_directory = cleanpath_aggressive(to_str(&base_directory));
16
15
 
17
- let mut dest_prefix = dest_directory.as_ref();
18
- let mut dest_names: Vec<&str> = vec![];
19
- loop {
20
- match chop_basename::chop_basename(&dest_prefix.clone()) {
21
- Some((ref dest, ref basename)) => {
22
- dest_prefix = dest;
23
- if basename != &"." {
24
- dest_names.push(basename)
25
- }
26
- },
27
- None => break,
28
- }
29
- }
30
- dest_names.reverse();
31
-
32
- let mut base_prefix = base_directory.as_ref();
33
- let mut base_names: Vec<&str> = vec![];
34
- loop {
35
- match chop_basename::chop_basename(&base_prefix) {
36
- Some((ref base, ref basename)) => {
37
- base_prefix = base;
38
- if basename != &"." {
39
- base_names.push(basename)
40
- }
41
- },
42
- None => break,
43
- }
44
- }
45
- base_names.reverse();
16
+ let (dest_prefix, mut dest_names) = to_names(dest_directory.as_ref());
17
+ let (base_prefix, mut base_names) = to_names(base_directory.as_ref());
46
18
 
47
19
  if !is_same_path(&dest_prefix, &base_prefix) {
48
20
  return Err(
49
21
  Exception::new(
50
22
  "ArgumentError",
51
- Some(&format!("different prefix: {} and {}", dest_prefix, base_prefix)[..])
23
+ Some(&format!("different prefix: {} and {}", dest_prefix, base_prefix)),
52
24
  )
53
25
  );
54
26
  }
55
27
 
56
- let (dest_names, base_names): (Vec<String>, Vec<String>) = dest_names.
57
- iter().
58
- zip_option(base_names.iter()).
59
- skip_while(|&(a, b)| {
60
- match (a, b) {
61
- (Some(x), Some(y)) => is_same_path(x,y),
62
- _ => false,
63
- }
64
- }).
65
- fold((vec![], vec![]), |mut acc, (a, b)| {
66
- if let Some(v) = a {
67
- acc.0.push(v.to_string());
68
- }
69
- if let Some(v) = b {
70
- acc.1.push(v.to_string());
71
- }
72
- acc
73
- });
28
+ // Remove shared tail
29
+ {
30
+ let num_same = dest_names.iter().rev().zip(base_names.iter().rev()).
31
+ take_while(|&(dest, base)| dest == base).count();
32
+ let num_dest_names = dest_names.len();
33
+ dest_names.truncate(num_dest_names - num_same);
34
+ let num_base_names = base_names.len();
35
+ base_names.truncate(num_base_names - num_same);
36
+ };
74
37
 
75
- if base_names.contains(&"..".to_string()) {
38
+ if base_names.contains(&"..") {
76
39
  return Err(
77
40
  Exception::new(
78
41
  "ArgumentError",
79
- Some(&format!("base_directory has ..: {}", base_directory)[..])
42
+ Some(&format!("base_directory has ..: {}", base_directory)),
80
43
  )
81
44
  );
82
45
  }
83
46
 
84
- let base_names: Vec<String> = base_names.into_iter().map(|_| "..".to_string()).collect();
85
-
86
47
  if base_names.is_empty() && dest_names.is_empty() {
87
48
  Ok(Pathname::new("."))
88
49
  } else {
89
- Ok(Pathname::new(&base_names.iter().chain(dest_names.iter()).map(String::as_str).
50
+ Ok(Pathname::new(&iter::repeat("..").take(base_names.len()).chain(dest_names.into_iter().rev()).
90
51
  collect::<Vec<&str>>().join(&SEP_STR)))
91
52
  }
92
53
  }
54
+
55
+ #[inline(always)]
56
+ fn to_names(path: &str) -> (&str, Vec<&str>) {
57
+ let mut result: Vec<&str> = vec![];
58
+ let mut prefix = path;
59
+ loop {
60
+ match chop_basename(&prefix) {
61
+ Some((ref dest, ref basename)) => {
62
+ prefix = dest;
63
+ if basename != &"." {
64
+ result.push(basename);
65
+ }
66
+ }
67
+ None => return (prefix, result),
68
+ }
69
+ }
70
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faster_path
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.3.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel P. Clark
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-12 00:00:00.000000000 Z
11
+ date: 2018-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler