faster_path 0.4.0 → 0.5.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 +4 -4
- data/Cargo.lock +3 -3
- data/Cargo.toml +2 -5
- data/README.md +53 -37
- data/Rakefile +6 -10
- data/ext/Rakefile +12 -6
- data/faster_path.gemspec +7 -6
- data/lib/faster_path/build_task.rb +32 -0
- data/lib/faster_path/optional/monkeypatches.rb +2 -2
- data/lib/faster_path/optional/refinements.rb +2 -2
- data/lib/faster_path/version.rb +1 -1
- data/lib/faster_path.rb +7 -12
- data/src/pathname.rs +45 -5
- metadata +29 -32
- data/lib/faster_path/thermite_initialize.rb +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9fb5780bd8ce8ea17d538b540ac69b1c023118d1351e167cde418ee48a4f8085
|
|
4
|
+
data.tar.gz: b12bc7a5ab8e4e26cfd46328bdb9e4b57eea367a9c40e76aeba74e12f06eb0e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fa615d0278378e74432832369cf56b16147ecc0e5e27bd12b1fd90c2690fab41dc68fd3042e8dd7c92b8ebb7741f7d08e9db25bd0cec61b384b520f9bd8b400a
|
|
7
|
+
data.tar.gz: 905389671e4062ef0824e7fb7af8d05fefb1df0ad2a5919bc8a54a8506309ba77cea577859ea49da4e15616fd2fb4bf1d025e5082baef6e61bf18c0c9799d000
|
data/Cargo.lock
CHANGED
|
@@ -4,7 +4,7 @@ version = 4
|
|
|
4
4
|
|
|
5
5
|
[[package]]
|
|
6
6
|
name = "faster_path"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
dependencies = [
|
|
9
9
|
"memchr",
|
|
10
10
|
"rutie",
|
|
@@ -30,9 +30,9 @@ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
|
|
30
30
|
|
|
31
31
|
[[package]]
|
|
32
32
|
name = "rutie"
|
|
33
|
-
version = "0.
|
|
33
|
+
version = "0.13.1"
|
|
34
34
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
35
|
-
checksum = "
|
|
35
|
+
checksum = "30d1529470ad0b69b81a6d0224632d00c01b46ef671452e24407512165b7ed84"
|
|
36
36
|
dependencies = [
|
|
37
37
|
"lazy_static",
|
|
38
38
|
"libc",
|
data/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "faster_path"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
authors = ["Daniel P. Clark <6ftdan@gmail.com>"]
|
|
5
5
|
description = "Alternative to Pathname"
|
|
6
6
|
homepage = "https://github.com/danielpclark/faster_path"
|
|
@@ -9,13 +9,10 @@ license = "MIT OR Apache-2.0"
|
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
edition = "2021"
|
|
11
11
|
|
|
12
|
-
[package.metadata.thermite]
|
|
13
|
-
github_releases = true
|
|
14
|
-
|
|
15
12
|
[lib]
|
|
16
13
|
name = "faster_path"
|
|
17
14
|
crate-type = ["cdylib"]
|
|
18
15
|
|
|
19
16
|
[dependencies]
|
|
20
|
-
rutie = "0.
|
|
17
|
+
rutie = "0.13.1"
|
|
21
18
|
memchr = "2"
|
data/README.md
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
[](https://github.com/danielpclark/faster_path/actions/workflows/ci.yml)
|
|
4
4
|
[](https://github.com/danielpclark/faster_path/tags)
|
|
5
5
|
[](https://github.com/danielpclark/faster_path/pulse)
|
|
6
|
-
[](https://github.com/danielpclark/faster_path/releases)
|
|
7
6
|
[](http://inch-ci.org/github/danielpclark/faster_path)
|
|
8
7
|
[](https://www.codetriage.com/danielpclark/faster_path)
|
|
9
8
|
|
|
@@ -114,14 +113,15 @@ As mentioned earlier Sprockets, which handles assets, changed away from using `P
|
|
|
114
113
|
|
|
115
114
|
## Requirements
|
|
116
115
|
|
|
117
|
-
* Ruby 2
|
|
118
|
-
* Rust 1.71 or later
|
|
116
|
+
* Ruby 3.2, 3.3 or 3.4 (the Rust extension is built with [Rutie](https://github.com/danielpclark/rutie) 0.13, which supports these)
|
|
117
|
+
* Rust 1.71 or later: `gem install` builds the extension with Cargo (through the
|
|
118
|
+
[rutie](https://rubygems.org/gems/rutie) gem's Rake task) and keeps only the built library
|
|
119
119
|
|
|
120
120
|
## Installation
|
|
121
121
|
|
|
122
|
-
Ensure Rust is installed
|
|
122
|
+
Ensure Rust is installed, with `cargo` on your `PATH`:
|
|
123
123
|
|
|
124
|
-
[Rust
|
|
124
|
+
[Install Rust](https://www.rust-lang.org/tools/install)
|
|
125
125
|
|
|
126
126
|
```
|
|
127
127
|
curl -sSf https://sh.rustup.rs | sh
|
|
@@ -130,7 +130,7 @@ curl -sSf https://sh.rustup.rs | sh
|
|
|
130
130
|
Add this line to your application's Gemfile:
|
|
131
131
|
|
|
132
132
|
```ruby
|
|
133
|
-
gem 'faster_path', '~> 0.
|
|
133
|
+
gem 'faster_path', '~> 0.5.0'
|
|
134
134
|
```
|
|
135
135
|
|
|
136
136
|
And then execute:
|
|
@@ -141,6 +141,12 @@ Or install it yourself as:
|
|
|
141
141
|
|
|
142
142
|
$ gem install faster_path
|
|
143
143
|
|
|
144
|
+
Installing compiles the Rust extension for the Ruby doing the installing ("Building native
|
|
145
|
+
extensions. This could take a while..."), which takes about a minute the first time while Cargo
|
|
146
|
+
fetches and builds the dependencies. If Rust is missing, the install fails at that step with
|
|
147
|
+
Cargo not being found; install Rust and run it again. Nothing is downloaded from GitHub: there are
|
|
148
|
+
no prebuilt binaries.
|
|
149
|
+
|
|
144
150
|
## Visual Benchmarks
|
|
145
151
|
|
|
146
152
|
Benchmarks in Faster Path now produce visual graph charts of performance improvements.
|
|
@@ -159,25 +165,25 @@ require "faster_path"
|
|
|
159
165
|
|
|
160
166
|
Current methods implemented:
|
|
161
167
|
|
|
162
|
-
|FasterPath Rust Implementation|Ruby Implementation|Time Shaved Off (Ruby
|
|
163
|
-
|
|
164
|
-
| `FasterPath.absolute?` | `Pathname#absolute?` |
|
|
165
|
-
| `FasterPath.add_trailing_separator` | `Pathname#add_trailing_separator` |
|
|
166
|
-
| `FasterPath.basename` | `File.basename` |
|
|
167
|
-
| `FasterPath.children` | `Pathname#children` |
|
|
168
|
-
| `FasterPath.chop_basename` | `Pathname#chop_basename` |
|
|
169
|
-
| `FasterPath.cleanpath_aggressive` | `Pathname#cleanpath_aggressive` |
|
|
170
|
-
| `FasterPath.cleanpath_conservative` | `Pathname#cleanpath_conservative` |
|
|
171
|
-
| `FasterPath.del_trailing_separator` | `Pathname#del_trailing_separator` |
|
|
172
|
-
| `FasterPath.directory?` | `Pathname#directory?` |
|
|
173
|
-
| `FasterPath.dirname` | `File.dirname` |
|
|
174
|
-
| `FasterPath.entries` | `Pathname#entries` |
|
|
175
|
-
| `FasterPath.extname` | `File.extname` |
|
|
176
|
-
| `FasterPath.has_trailing_separator?` | `Pathname#has_trailing_separator` |
|
|
177
|
-
| `FasterPath.join` | `Pathname#join` |
|
|
178
|
-
| `FasterPath.plus` | `Pathname#plus` |
|
|
179
|
-
| `FasterPath.relative?` | `Pathname#relative?` |
|
|
180
|
-
| `FasterPath.relative_path_from` | `Pathname#relative_path_from` |
|
|
168
|
+
|FasterPath Rust Implementation|Ruby Implementation|Time Shaved Off (Ruby 3.4)|Time Shaved Off (Ruby 3.3)|Time Shaved Off (Ruby 3.2)|
|
|
169
|
+
|---|---|:---:|:---:|:---:|
|
|
170
|
+
| `FasterPath.absolute?` | `Pathname#absolute?` | 55.5% | 53.2% | 43.1% |
|
|
171
|
+
| `FasterPath.add_trailing_separator` | `Pathname#add_trailing_separator` | 87.4% | 85.3% | 85.8% |
|
|
172
|
+
| `FasterPath.basename` | `File.basename` | 30.7% | 22.2% | 27.5% |
|
|
173
|
+
| `FasterPath.children` | `Pathname#children` | 65.0% | 55.1% | 55.4% |
|
|
174
|
+
| `FasterPath.chop_basename` | `Pathname#chop_basename` | 75.3% | 73.5% | 72.8% |
|
|
175
|
+
| `FasterPath.cleanpath_aggressive` | `Pathname#cleanpath_aggressive` | 92.4% | 92.2% | 92.4% |
|
|
176
|
+
| `FasterPath.cleanpath_conservative` | `Pathname#cleanpath_conservative` | 91.4% | 91.3% | 91.8% |
|
|
177
|
+
| `FasterPath.del_trailing_separator` | `Pathname#del_trailing_separator` | 89.1% | 87.9% | 87.8% |
|
|
178
|
+
| `FasterPath.directory?` | `Pathname#directory?` | 41.5% | 39.9% | 44.2% |
|
|
179
|
+
| `FasterPath.dirname` | `File.dirname` | 52.0% | 40.3% | 40.8% |
|
|
180
|
+
| `FasterPath.entries` | `Pathname#entries` | 57.8% | 42.4% | 45.9% |
|
|
181
|
+
| `FasterPath.extname` | `File.extname` | 69.4% | 66.7% | 69.2% |
|
|
182
|
+
| `FasterPath.has_trailing_separator?` | `Pathname#has_trailing_separator` | 93.4% | 92.8% | 91.7% |
|
|
183
|
+
| `FasterPath.join` | `Pathname#join` | 89.1% | 89.0% | 88.4% |
|
|
184
|
+
| `FasterPath.plus` | `Pathname#plus` | 95.5% | 94.7% | 94.5% |
|
|
185
|
+
| `FasterPath.relative?` | `Pathname#relative?` | 66.9% | 66.3% | 60.4% |
|
|
186
|
+
| `FasterPath.relative_path_from` | `Pathname#relative_path_from` | 94.4% | 94.6% | 94.2% |
|
|
181
187
|
|
|
182
188
|
See [Benchmarks](#benchmarks) for how these are measured.
|
|
183
189
|
|
|
@@ -205,10 +211,10 @@ FasterPath.sledgehammer_everything!
|
|
|
205
211
|
|
|
206
212
|
These will **not** be included by default in monkey-patches. To try them with monkeypatching use the environment flag of `WITH_REGRESSION`. These methods are here to be improved upon.
|
|
207
213
|
|
|
208
|
-
|FasterPath Implementation|Ruby Implementation|Time Shaved Off (Ruby
|
|
209
|
-
|
|
210
|
-
| `FasterPath.entries_compat` | `Pathname.entries` |
|
|
211
|
-
| `FasterPath.children_compat` | `Pathname.children` |
|
|
214
|
+
|FasterPath Implementation|Ruby Implementation|Time Shaved Off (Ruby 3.4)|Time Shaved Off (Ruby 3.3)|Time Shaved Off (Ruby 3.2)|
|
|
215
|
+
|---|---|:---:|:---:|:---:|
|
|
216
|
+
| `FasterPath.entries_compat` | `Pathname.entries` | 35.7% | 22.0% | 15.9% |
|
|
217
|
+
| `FasterPath.children_compat` | `Pathname.children` | 46.7% | 33.6% | 32.3% |
|
|
212
218
|
|
|
213
219
|
It's been my observation (and some others) that the Rust implementation of the C code for `File` has similar results but
|
|
214
220
|
performance seems to vary based on CPU cache on possibly 64bit/32bit system environments. These are not included by default when the monkey patch method `FasterPath.sledgehammer_everything!` is executed.
|
|
@@ -216,13 +222,19 @@ performance seems to vary based on CPU cache on possibly 64bit/32bit system envi
|
|
|
216
222
|
## Benchmarks
|
|
217
223
|
|
|
218
224
|
The "Time Shaved Off" figures come from the project's Pinch-bench (`rake pbench`, in
|
|
219
|
-
`test/pbench`): each method runs against Ruby's own implementation,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
225
|
+
`test/pbench`): each method runs against Ruby's own implementation, alternating with it round by
|
|
226
|
+
round, and the result is how much less time FasterPath's best round took than Ruby's best round.
|
|
227
|
+
They are the median of 3 runs of `LONG_RUN=30 rake pbench` (5 rounds of 300,000 calls of each
|
|
228
|
+
method) on Linux x86_64, each run pinned to one CPU core, with Ruby 3.4.6, 3.3.6 and 3.2.9, built
|
|
229
|
+
with Rust 1.97. The three runs agree within a few points for most methods; the ones that touch
|
|
230
|
+
the filesystem (`children`, `directory?`, `entries`) vary the most.
|
|
231
|
+
|
|
232
|
+
The figures are smaller than they were on Ruby 2 for the cheapest methods, `absolute?` and
|
|
233
|
+
`relative?` above all: Ruby 3's own `Pathname#absolute?` is a single regexp match, where Ruby 2's
|
|
234
|
+
walked the path with `chop_basename`, so there is less left to shave off.
|
|
223
235
|
|
|
224
236
|
`rake pbench` is also the performance regression test: each method has a floor in
|
|
225
|
-
`test/pbench/pbench_suite.rb` (about half of
|
|
237
|
+
`test/pbench/pbench_suite.rb` (about half of its lowest figure in the table above) and the task
|
|
226
238
|
fails when a method measures below it, so a change that makes a method slower shows up in
|
|
227
239
|
`rake test`. On Windows, where `join` is currently slower than Ruby's
|
|
228
240
|
([#185](https://github.com/danielpclark/faster_path/issues/185)), the floors are reported but not
|
|
@@ -230,8 +242,10 @@ enforced.
|
|
|
230
242
|
|
|
231
243
|
### Before and after the Rutie 0.10 upgrade
|
|
232
244
|
|
|
233
|
-
|
|
234
|
-
|
|
245
|
+
The last figures on Ruby 2, from the Rutie 0.10 upgrade (faster_path 0.4.0): Ruby 2.7.8 and
|
|
246
|
+
2.5.7, alternating runs of the code before the upgrade (built with Rutie 0.6) and after it, with
|
|
247
|
+
the benchmark then measuring the average of 5 rounds run one implementation after the other.
|
|
248
|
+
Higher is better; the change is in percentage points.
|
|
235
249
|
|
|
236
250
|
|Method|Ruby 2.7 before|Ruby 2.7 after|Change|Ruby 2.5 before|Ruby 2.5 after|Change|
|
|
237
251
|
|---|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
@@ -278,7 +292,9 @@ Rust method like `!absolute?` _(not absolute)_ drops 39% of the performance alre
|
|
|
278
292
|
Whenever feasible implement it in Rust.
|
|
279
293
|
|
|
280
294
|
After checking out the repo, make sure you have Rust installed, then run `bundle`.
|
|
281
|
-
Run `rake test` to run the tests, and `rake bench` for benchmarks.
|
|
295
|
+
Run `rake test` to run the tests, and `rake bench` for benchmarks. Both build the extension
|
|
296
|
+
first (`rake rutie:build`, into `target/release`, which is where `require 'faster_path'` loads it
|
|
297
|
+
from in a checkout).
|
|
282
298
|
|
|
283
299
|
### Building and running tests
|
|
284
300
|
|
data/Rakefile
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'bundler/gem_tasks'
|
|
2
2
|
require 'rake/testtask'
|
|
3
|
-
require '
|
|
4
|
-
require 'faster_path/
|
|
3
|
+
require 'faster_path/version'
|
|
4
|
+
require 'faster_path/build_task'
|
|
5
5
|
|
|
6
6
|
desc 'System Details'
|
|
7
7
|
task :sysinfo do
|
|
@@ -22,7 +22,7 @@ task :sysinfo do
|
|
|
22
22
|
puts "RAKE\n #{Rake::VERSION}"
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
FasterPath::BuildTask.define
|
|
26
26
|
|
|
27
27
|
desc "Generate Contriburs.md Manifest"
|
|
28
28
|
task :contrib do
|
|
@@ -31,10 +31,8 @@ task :contrib do
|
|
|
31
31
|
' > Contributors.md;git shortlog -s -n -e | sed 's/^......./- /' >> Contributors.md"
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
desc 'Build
|
|
35
|
-
task build_lib: '
|
|
36
|
-
thermite.run_cargo 'clean'
|
|
37
|
-
end
|
|
34
|
+
desc 'Build the Rust extension'
|
|
35
|
+
task build_lib: 'rutie:build'
|
|
38
36
|
|
|
39
37
|
desc 'Code Quality Check'
|
|
40
38
|
task :lint do
|
|
@@ -92,10 +90,8 @@ Rake::TestTask.new(bench: :build_lib) do |t|
|
|
|
92
90
|
t.pattern = 'test/**/*_benchmark.rb'
|
|
93
91
|
end
|
|
94
92
|
|
|
93
|
+
# LONG_RUN=<1..30> multiplies the calls per round, see test/pbench/pbench.rb
|
|
95
94
|
Rake::TestTask.new(pbench: :build_lib) do |t|
|
|
96
|
-
if ARGV.last == '--long-run'
|
|
97
|
-
ENV['LONG_RUN'] = '10'
|
|
98
|
-
end
|
|
99
95
|
t.libs = %w[lib test test/pbench]
|
|
100
96
|
t.pattern = 'test/pbench/pbench_suite.rb'
|
|
101
97
|
end
|
data/ext/Rakefile
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require_relative '../lib/faster_path/
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require_relative '../lib/faster_path/build_task'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
Thermite::Tasks.new(cargo_project_path: project_toplevel_dir,
|
|
6
|
-
ruby_project_path: project_toplevel_dir)
|
|
4
|
+
FasterPath::BuildTask.define
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
# Keeps the built library (in lib/faster_path, where FasterPath loads it from)
|
|
7
|
+
# and removes the rest of Cargo's output from the installed gem.
|
|
8
|
+
task install_library: 'rutie:build' do
|
|
9
|
+
FileUtils.mkdir_p(FasterPath::BuildTask::INSTALLED_LIBRARY_DIR)
|
|
10
|
+
FileUtils.cp(FasterPath::BuildTask.built_library, FasterPath::BuildTask::INSTALLED_LIBRARY_DIR)
|
|
11
|
+
Rake::Task['rutie:clean'].invoke
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
task default: :install_library
|
data/faster_path.gemspec
CHANGED
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
|
16
16
|
spec.files = [
|
|
17
17
|
'Cargo.lock', 'Cargo.toml', 'Gemfile', 'MIT-LICENSE.txt', 'README.md', 'Rakefile',
|
|
18
18
|
'bin/console', 'bin/setup', 'ext/Rakefile', 'faster_path.gemspec', 'lib/faster_path.rb',
|
|
19
|
-
'lib/faster_path/version.rb', 'lib/faster_path/
|
|
19
|
+
'lib/faster_path/version.rb', 'lib/faster_path/build_task.rb',
|
|
20
20
|
'lib/faster_path/optional/monkeypatches.rb', 'lib/faster_path/optional/refinements.rb'
|
|
21
21
|
]
|
|
22
22
|
spec.files += Dir['src/**/*']
|
|
@@ -24,17 +24,18 @@ Gem::Specification.new do |spec|
|
|
|
24
24
|
spec.extensions = ['ext/Rakefile']
|
|
25
25
|
spec.require_paths = ['lib']
|
|
26
26
|
|
|
27
|
-
# Rutie 0.
|
|
28
|
-
spec.required_ruby_version = ['>= 2
|
|
27
|
+
# Rutie 0.13, which the Rust extension is built with, supports Ruby 3.2 to 3.4
|
|
28
|
+
spec.required_ruby_version = ['>= 3.2', '< 3.5']
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
# The extension is built at install time with Rutie's Rake task and loaded with Rutie
|
|
31
31
|
spec.add_dependency 'rake', '>= 12.3'
|
|
32
|
-
spec.add_dependency '
|
|
32
|
+
spec.add_dependency 'rutie', '~> 0.0.5'
|
|
33
33
|
spec.add_development_dependency 'read_source', '~> 0.2.6'
|
|
34
34
|
spec.add_development_dependency 'minitest', '~> 5.11'
|
|
35
35
|
spec.add_development_dependency 'minitest-reporters', '~> 1.1'
|
|
36
36
|
spec.add_development_dependency 'color_pound_spec_reporter', '~> 0.0.9'
|
|
37
|
-
spec.add_development_dependency 'rubocop', '
|
|
37
|
+
spec.add_development_dependency 'rubocop', '~> 1.80'
|
|
38
|
+
spec.add_development_dependency 'rubocop-performance', '~> 1.20'
|
|
38
39
|
spec.add_development_dependency 'stop_watch', '~> 1.0'
|
|
39
40
|
if !ENV['CI'] && ENV['GRAPH']
|
|
40
41
|
spec.add_development_dependency 'gruff', '~> 0.7.0'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'rbconfig'
|
|
2
|
+
require 'rutie'
|
|
3
|
+
require 'rutie/rake_task'
|
|
4
|
+
|
|
5
|
+
module FasterPath
|
|
6
|
+
# Defines `rutie:build` and `rutie:clean`, which build the Rust extension
|
|
7
|
+
# with Cargo. Used by the Rakefile and by ext/Rakefile when the gem is
|
|
8
|
+
# installed; both may run from another directory, so Cargo is pointed at
|
|
9
|
+
# the gem's Cargo.toml.
|
|
10
|
+
#
|
|
11
|
+
# The Rakefile leaves the library in target/release, where Rutie loads it
|
|
12
|
+
# from; ext/Rakefile moves it to lib/faster_path so that the installed gem
|
|
13
|
+
# keeps nothing else of the build.
|
|
14
|
+
module BuildTask
|
|
15
|
+
PROJECT_DIR = File.expand_path('../..', __dir__)
|
|
16
|
+
# Where `gem install` puts the built library
|
|
17
|
+
INSTALLED_LIBRARY_DIR = File.join(PROJECT_DIR, 'lib', 'faster_path')
|
|
18
|
+
|
|
19
|
+
def self.define
|
|
20
|
+
# Rutie's build script links the extension to the Ruby named by RUBY,
|
|
21
|
+
# or the first `ruby` on the PATH; make it the Ruby running Rake.
|
|
22
|
+
ENV['RUBY'] ||= File.join(RbConfig::CONFIG['bindir'],
|
|
23
|
+
RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT'])
|
|
24
|
+
Rutie::RakeTask.new(cargo_args: ['--manifest-path', File.join(PROJECT_DIR, 'Cargo.toml')])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The library `rutie:build` builds, in the project's target/release
|
|
28
|
+
def self.built_library
|
|
29
|
+
Rutie.new(:faster_path).ffi_library(File.join(PROJECT_DIR, 'lib'))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -24,10 +24,10 @@ module FasterPath
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
# @private :nodoc:
|
|
27
|
-
def self.dirname(pth)
|
|
27
|
+
def self.dirname(pth, level = 1)
|
|
28
28
|
pth = pth.to_path if pth.respond_to? :to_path
|
|
29
29
|
raise TypeError unless pth.is_a? String
|
|
30
|
-
FasterPath.dirname(pth)
|
|
30
|
+
FasterPath.dirname(pth, level)
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -20,10 +20,10 @@ module FasterPath
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# @private :nodoc:
|
|
23
|
-
def self.dirname(pth)
|
|
23
|
+
def self.dirname(pth, level = 1)
|
|
24
24
|
pth = pth.to_path if pth.respond_to? :to_path
|
|
25
25
|
raise TypeError unless pth.is_a? String
|
|
26
|
-
FasterPath.dirname(pth)
|
|
26
|
+
FasterPath.dirname(pth, level)
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
data/lib/faster_path/version.rb
CHANGED
data/lib/faster_path.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'faster_path/version'
|
|
2
2
|
require 'pathname'
|
|
3
|
-
require '
|
|
4
|
-
require 'faster_path/thermite_initialize'
|
|
3
|
+
require 'rutie'
|
|
5
4
|
require 'fiddle'
|
|
6
5
|
require 'fiddle/import'
|
|
7
6
|
|
|
@@ -14,16 +13,12 @@ require 'fiddle/import'
|
|
|
14
13
|
# existing code ecosystem. To do so you will need to
|
|
15
14
|
# `require 'faster_path/optional/monkeypatches'` beforehand.
|
|
16
15
|
module FasterPath
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Fiddle::Function.
|
|
25
|
-
new(Fiddle.dlopen(FFI_LIBRARY)['Init_faster_pathname'], [], Fiddle::TYPE_VOID).
|
|
26
|
-
call
|
|
16
|
+
# The Rust extension: in the project's target/release after `rake rutie:build`,
|
|
17
|
+
# or in lib/faster_path, where `gem install` puts it (see ext/Rakefile)
|
|
18
|
+
RUTIE = [Rutie.new(:faster_path), Rutie.new(:faster_path, lib_path: 'faster_path')].
|
|
19
|
+
find { |rutie| File.exist?(rutie.ffi_library(__dir__)) } || Rutie.new(:faster_path)
|
|
20
|
+
FFI_LIBRARY = RUTIE.ffi_library(__dir__)
|
|
21
|
+
RUTIE.init('Init_faster_pathname', __dir__)
|
|
27
22
|
|
|
28
23
|
Public.class_eval do
|
|
29
24
|
private_class_method :basename
|
data/src/pathname.rs
CHANGED
|
@@ -3,7 +3,7 @@ use std::ffi::OsStr;
|
|
|
3
3
|
use std::fs;
|
|
4
4
|
use std::io;
|
|
5
5
|
|
|
6
|
-
use rutie::{AnyException, AnyObject, Array, Boolean, Class, NilClass, Object, RString};
|
|
6
|
+
use rutie::{AnyException, AnyObject, Array, Boolean, Class, Integer, NilClass, Object, RString, Symbol};
|
|
7
7
|
|
|
8
8
|
use crate::basename;
|
|
9
9
|
use crate::chop_basename::{self, is_relative};
|
|
@@ -15,7 +15,7 @@ use crate::path_parsing::Rules;
|
|
|
15
15
|
use crate::plus;
|
|
16
16
|
use crate::relative_path_from::{self, RelativePathError};
|
|
17
17
|
use crate::ruby::{
|
|
18
|
-
argument_error, check_max_arguments, error, new_pathname, path_like_to_string, pathname_class,
|
|
18
|
+
argument_error, check_max_arguments, error, inspect, new_pathname, path_like_to_string, pathname_class,
|
|
19
19
|
truthy_argument, EncodingId, EncodingOf, PathArgument, PathString, RubyResult,
|
|
20
20
|
};
|
|
21
21
|
|
|
@@ -152,11 +152,51 @@ pub fn pn_is_directory(arguments: &[AnyObject]) -> RubyResult {
|
|
|
152
152
|
Ok(Boolean::new(is_directory).into())
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// `File.dirname(path, level = 1)`: `level` is the number of trailing
|
|
156
|
+
// components to remove (Ruby 3.1+).
|
|
155
157
|
pub fn pn_dirname(arguments: &[AnyObject]) -> RubyResult {
|
|
156
|
-
check_max_arguments(arguments,
|
|
158
|
+
check_max_arguments(arguments, 2)?;
|
|
157
159
|
let argument = PathArgument::new(arguments, 0)?;
|
|
158
160
|
let path = argument.path()?;
|
|
159
|
-
|
|
161
|
+
let level = match arguments.get(1) {
|
|
162
|
+
Some(level) => level_argument(level)?,
|
|
163
|
+
None => 1,
|
|
164
|
+
};
|
|
165
|
+
let mut result: Cow<[u8]> = Cow::Borrowed(path.bytes());
|
|
166
|
+
for _ in 0..level {
|
|
167
|
+
let parent = dirname::dirname(RULES, &result);
|
|
168
|
+
if *parent == *result {
|
|
169
|
+
// The root ("/", "." or "C:/"), which is its own dirname
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
result = Cow::Owned(parent.into_owned());
|
|
173
|
+
}
|
|
174
|
+
if result.is_empty() {
|
|
175
|
+
// `File.dirname("", 0)` is "." like `File.dirname("")`
|
|
176
|
+
result = Cow::Borrowed(b".");
|
|
177
|
+
}
|
|
178
|
+
Ok(path.to_ruby(&result).into())
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// The `level` argument of `File.dirname`, converted like Ruby's `NUM2INT`.
|
|
182
|
+
fn level_argument(object: &AnyObject) -> Result<usize, AnyException> {
|
|
183
|
+
let integer = match object.try_convert_to::<Integer>() {
|
|
184
|
+
Ok(integer) => integer,
|
|
185
|
+
Err(_) => {
|
|
186
|
+
let responds = object.protect_send("respond_to?", &[Symbol::new("to_int").into()])?;
|
|
187
|
+
if !responds.value().is_true() {
|
|
188
|
+
let class_name = object.protect_send("class", &[]).ok().and_then(|class| inspect(&class).ok()).unwrap_or_default();
|
|
189
|
+
return Err(error(&Class::type_error(), &format!("no implicit conversion of {} into Integer", class_name)));
|
|
190
|
+
}
|
|
191
|
+
let converted = object.protect_send("to_int", &[])?;
|
|
192
|
+
converted.try_convert_to::<Integer>()?
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
let level = integer.to_i64();
|
|
196
|
+
if level < 0 {
|
|
197
|
+
return Err(argument_error(&format!("negative level: {}", level)));
|
|
198
|
+
}
|
|
199
|
+
Ok(level as usize)
|
|
160
200
|
}
|
|
161
201
|
|
|
162
202
|
// Returns an array of `String`s
|
|
@@ -315,7 +355,7 @@ fn system_call_error(err: &io::Error, path: &AnyObject, func: &str) -> AnyExcept
|
|
|
315
355
|
#[cfg(unix)]
|
|
316
356
|
{
|
|
317
357
|
if let Some(errno) = err.raw_os_error() {
|
|
318
|
-
let arguments = [path.clone(),
|
|
358
|
+
let arguments = [path.clone(), Integer::new(errno.into()).into(), RString::new_utf8(func).into()];
|
|
319
359
|
if let Ok(exception) = Class::system_call_error().protect_send("new", &arguments) {
|
|
320
360
|
if let Ok(exception) = exception.try_convert_to::<AnyException>() {
|
|
321
361
|
return exception;
|
metadata
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: faster_path
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel P. Clark
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
|
-
- !ruby/object:Gem::Dependency
|
|
14
|
-
name: bundler
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - ">="
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.12'
|
|
20
|
-
type: :runtime
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - ">="
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.12'
|
|
27
12
|
- !ruby/object:Gem::Dependency
|
|
28
13
|
name: rake
|
|
29
14
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -39,19 +24,19 @@ dependencies:
|
|
|
39
24
|
- !ruby/object:Gem::Version
|
|
40
25
|
version: '12.3'
|
|
41
26
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
27
|
+
name: rutie
|
|
43
28
|
requirement: !ruby/object:Gem::Requirement
|
|
44
29
|
requirements:
|
|
45
|
-
- -
|
|
30
|
+
- - "~>"
|
|
46
31
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: 0.
|
|
32
|
+
version: 0.0.5
|
|
48
33
|
type: :runtime
|
|
49
34
|
prerelease: false
|
|
50
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
36
|
requirements:
|
|
52
|
-
- -
|
|
37
|
+
- - "~>"
|
|
53
38
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.
|
|
39
|
+
version: 0.0.5
|
|
55
40
|
- !ruby/object:Gem::Dependency
|
|
56
41
|
name: read_source
|
|
57
42
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -112,16 +97,30 @@ dependencies:
|
|
|
112
97
|
name: rubocop
|
|
113
98
|
requirement: !ruby/object:Gem::Requirement
|
|
114
99
|
requirements:
|
|
115
|
-
- -
|
|
100
|
+
- - "~>"
|
|
116
101
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '
|
|
102
|
+
version: '1.80'
|
|
118
103
|
type: :development
|
|
119
104
|
prerelease: false
|
|
120
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
106
|
requirements:
|
|
122
|
-
- -
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '1.80'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: rubocop-performance
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '1.20'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
123
122
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '
|
|
123
|
+
version: '1.20'
|
|
125
124
|
- !ruby/object:Gem::Dependency
|
|
126
125
|
name: stop_watch
|
|
127
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -155,9 +154,9 @@ files:
|
|
|
155
154
|
- ext/Rakefile
|
|
156
155
|
- faster_path.gemspec
|
|
157
156
|
- lib/faster_path.rb
|
|
157
|
+
- lib/faster_path/build_task.rb
|
|
158
158
|
- lib/faster_path/optional/monkeypatches.rb
|
|
159
159
|
- lib/faster_path/optional/refinements.rb
|
|
160
|
-
- lib/faster_path/thermite_initialize.rb
|
|
161
160
|
- lib/faster_path/version.rb
|
|
162
161
|
- src/basename.rs
|
|
163
162
|
- src/chop_basename.rs
|
|
@@ -177,7 +176,6 @@ homepage: https://github.com/danielpclark/faster_path
|
|
|
177
176
|
licenses:
|
|
178
177
|
- MIT OR Apache-2.0
|
|
179
178
|
metadata: {}
|
|
180
|
-
post_install_message:
|
|
181
179
|
rdoc_options: []
|
|
182
180
|
require_paths:
|
|
183
181
|
- lib
|
|
@@ -185,18 +183,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
185
183
|
requirements:
|
|
186
184
|
- - ">="
|
|
187
185
|
- !ruby/object:Gem::Version
|
|
188
|
-
version: '2
|
|
186
|
+
version: '3.2'
|
|
189
187
|
- - "<"
|
|
190
188
|
- !ruby/object:Gem::Version
|
|
191
|
-
version: '3.
|
|
189
|
+
version: '3.5'
|
|
192
190
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
191
|
requirements:
|
|
194
192
|
- - ">="
|
|
195
193
|
- !ruby/object:Gem::Version
|
|
196
194
|
version: '0'
|
|
197
195
|
requirements: []
|
|
198
|
-
rubygems_version: 3.
|
|
199
|
-
signing_key:
|
|
196
|
+
rubygems_version: 3.6.9
|
|
200
197
|
specification_version: 4
|
|
201
198
|
summary: Reimplementation of Pathname for better performance
|
|
202
199
|
test_files: []
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
require 'rubygems'
|
|
2
|
-
require 'rake/tasklib'
|
|
3
|
-
require_relative './version'
|
|
4
|
-
|
|
5
|
-
# @private :nodoc:
|
|
6
|
-
module Thermite
|
|
7
|
-
# @private :nodoc:
|
|
8
|
-
class Config
|
|
9
|
-
# @private :nodoc:
|
|
10
|
-
def ruby_version
|
|
11
|
-
"ruby#{RUBY_VERSION}"
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# @private :nodoc:
|
|
16
|
-
class Tasks < Rake::TaskLib
|
|
17
|
-
# @private :nodoc:
|
|
18
|
-
def github_download_uri(_tag, version)
|
|
19
|
-
"#{github_uri}/releases/download/v#{FasterPath::VERSION}/#{config.tarball_filename(version)}"
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|