faster_path 0.3.9 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3a5d299440e18a99c763e17b085b7962bfca0f2555b0adf8eb053c77b2d8d282
4
- data.tar.gz: fee3ac374b164a25d1af43e0e8cd36ea68f57421db38637b7275a504e38bb391
3
+ metadata.gz: 32af4c3fa17e05003792d14b3e8a34ce26fd206395f345fc58a13c8cc63b37b7
4
+ data.tar.gz: 7ec503bf0957f176fa5daa1d4910a67af9c3b06bb391e573586fcaadc1157a88
5
5
  SHA512:
6
- metadata.gz: 79d7b3931800982e78cc7f00c2fd2281932a2ac3775784f5f9d5c59bf974cd41f30d2d03c6216ce210fdc1754659b714cda6c19cca84e39a464e2bb62f7afa9c
7
- data.tar.gz: 1a5b56dff703996bdcf60838ad9881528e51867aa2e9c5d996ff9c28996fb1297e7dacc9380a42def9ff9358e87ac4576a2f7536d5c73b685332b73b9e687122
6
+ metadata.gz: ff3f4a219cb46f561eec9a2138d1282773ad2ceddcd0664202c2396202efbb1ecb2b8839347dd65bcf595faf93b6d6d6b85d6a912c18380b8ff10ccd878379ab
7
+ data.tar.gz: 27e3886927a1479b5d28e1b2a5d96cb5bb86c240b87664010b1be9d181afebf456476a43c1e7843f890b2af581ccd398dca91c3dd6f1021380e156402f2d90ab
data/Cargo.lock CHANGED
@@ -1,13 +1,7 @@
1
- [[package]]
2
- name = "array_tool"
3
- version = "1.0.3"
4
- source = "registry+https://github.com/rust-lang/crates.io-index"
5
-
6
1
  [[package]]
7
2
  name = "faster_path"
8
3
  version = "0.0.1"
9
4
  dependencies = [
10
- "array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
11
5
  "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
12
6
  "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
13
7
  "ruby-sys 0.3.0 (git+https://github.com/danielpclark/ruby-sys?branch=playground)",
@@ -55,7 +49,6 @@ dependencies = [
55
49
  ]
56
50
 
57
51
  [metadata]
58
- "checksum array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271"
59
52
  "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
60
53
  "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
61
54
  "checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff"
data/Cargo.toml CHANGED
@@ -18,6 +18,5 @@ crate-type = ["dylib"]
18
18
  [dependencies]
19
19
  ruby-sys = { git = "https://github.com/danielpclark/ruby-sys", branch = "playground" }
20
20
  ruru = { git = "https://github.com/danielpclark/ruru", branch = "playground" }
21
- array_tool = "1.0"
22
21
  lazy_static = "1.0"
23
22
  memchr = "2.0.1"
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source 'https://rubygems.org' do
2
2
  # Specify your gem's dependencies in faster_path.gemspec
3
3
  gemspec
4
+
4
5
  group :test do
5
6
  gem 'coveralls', require: false
6
7
  end
data/README.md CHANGED
@@ -3,12 +3,11 @@
3
3
  [![TravisCI Build Status](https://travis-ci.org/danielpclark/faster_path.svg?branch=master)](https://travis-ci.org/danielpclark/faster_path)
4
4
  [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/10ul0gk3cwhlt2lj/branch/master?svg=true)](https://ci.appveyor.com/project/danielpclark/faster-path/branch/master)
5
5
  [![Latest Tag](https://img.shields.io/github/tag/danielpclark/faster_path.svg)](https://github.com/danielpclark/faster_path/tags)
6
- [![Commits Since Last Release](https://img.shields.io/github/commits-since/danielpclark/faster_path/v0.3.8.svg)](https://github.com/danielpclark/faster_path/pulse)
6
+ [![Commits Since Last Release](https://img.shields.io/github/commits-since/danielpclark/faster_path/v0.3.10.svg)](https://github.com/danielpclark/faster_path/pulse)
7
7
  [![Binary Release](https://img.shields.io/github/release/danielpclark/faster_path.svg)](https://github.com/danielpclark/faster_path/releases)
8
8
  [![Coverage Status](https://coveralls.io/repos/github/danielpclark/faster_path/badge.svg?branch=master)](https://coveralls.io/github/danielpclark/faster_path?branch=master)
9
9
  [![Inline docs](http://inch-ci.org/github/danielpclark/faster_path.svg?branch=master)](http://inch-ci.org/github/danielpclark/faster_path)
10
10
  [![Code Triagers Badge](https://www.codetriage.com/danielpclark/faster_path/badges/users.svg)](https://www.codetriage.com/danielpclark/faster_path)
11
- [![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.)
12
11
 
13
12
  #### This gem shaves off more than 30% of my Rails application page load time.
14
13
 
@@ -94,6 +93,18 @@ I've said this about Sprockets but this required two other gems to be updated as
94
93
  |sass 3.2.19|sass 5.0.4|
95
94
  |bootstrap-sass 3.3.4.1|bootstrap-sass 3.3.6|
96
95
 
96
+ ## Performance Specifics
97
+
98
+ The headline for the amount for improvement on this library is specific to only the improvement made with the method `chop_basename`. Just so you know; in my initial release I had a bug in which that method immediately returned nothing. Now the good thing about this is that it gave me some very valuable information. First I found that all my Rails site tests still passed. Second I found that all my assets no longer loaded in the website. And third, and most importantly, I found my Rails web pages loaded just more than 66% faster without the cost of time that `chop_basename` took.
99
+
100
+ **That's right; the path handling for assets in your website \*consumes more than 2/3rds of your websites page load time.**
101
+
102
+ So now we have some real numbers to work with We can be generoues and use 66% as our margin of area to improve over _(for `chop_basename` specifically, not counting the benefit from improving the performance in other file path related methods)_. That means we want to remove as much of that percentage from the overall systems page load time. The original headline boasts over 33% performance improvement — that was when `chop_basename` was improved by just over 50%. Now `chop_basename` is improved by 83.4%. That alone should make your site run 55.044% faster now _(given your performance profile stats are similar to mine)_.
103
+
104
+ ## What Rails Versions Will This Apply To?
105
+
106
+ As mentioned earlier Sprockets, which handles assets, changed away from using `Pathname` at all when moving from major version 2 to 3. So if you're using Sprockets 3 or later you won't reap the biggest performance reqards from using this gem for now _(it's my goal to have this project become a core feature that Rails depends on and yes… that's a big ask)_. That is unless you write you're own implementation to re-integrate the use of `Pathname` and `FasterPath` into your asset handling library. For now just know that the Sprockets 2 series primarily works with Rails 4.1 and earlier. It may work in later Rails versions but I have not investigated this.
107
+
97
108
  ## Status
98
109
 
99
110
  * Rust compilation is working
@@ -115,7 +126,7 @@ curl -sSf https://static.rust-lang.org/rustup.sh | sh
115
126
  Add this line to your application's Gemfile:
116
127
 
117
128
  ```ruby
118
- gem 'faster_path', '~> 0.3.8'
129
+ gem 'faster_path', '~> 0.3.10'
119
130
  ```
120
131
 
121
132
  And then execute:
@@ -153,8 +164,8 @@ Current methods implemented:
153
164
  | `FasterPath.entries` | `Pathname#entries` | 41.0% |
154
165
  | `FasterPath.extname` | `File.extname` | 63.1% |
155
166
  | `FasterPath.has_trailing_separator?` | `Pathname#has_trailing_separator` | 88.9% |
156
- | `FasterPath.plus` | `Pathname#join` | 72.0% |
157
- | `FasterPath.plus` | `Pathname#plus` | 87.7% |
167
+ | `FasterPath.plus` | `Pathname#join` | 79.1% |
168
+ | `FasterPath.plus` | `Pathname#plus` | 94.7% |
158
169
  | `FasterPath.relative?` | `Pathname#relative?` | 92.6% |
159
170
  | `FasterPath.relative_path_from` | `Pathname#relative_path_from` | 93.3% |
160
171
 
@@ -205,8 +216,6 @@ Whenever feasible implement it in Rust.
205
216
  After checking out the repo, make sure you have Rust installed, then run `bundle`.
206
217
  Run `rake test` to run the tests, and `rake bench` for benchmarks.
207
218
 
208
- Learn and share performance tips on the [wiki](https://github.com/danielpclark/faster_path/wiki)!
209
-
210
219
  ### Building and running tests
211
220
 
212
221
  First, bundle the gem's development dependencies by running `bundle`. Rust compilation is included in the current rake commands.
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = ['lib']
26
26
 
27
27
  spec.add_dependency 'bundler', '~> 1.12'
28
- spec.add_dependency 'rake', '~> 12.0'
28
+ spec.add_dependency 'rake', '~> 12.3'
29
29
  spec.add_dependency 'thermite', '0.13.0'
30
30
  spec.add_development_dependency 'read_source', '~> 0.2.6'
31
31
  spec.add_development_dependency 'minitest', '~> 5.10'
@@ -6,8 +6,13 @@ require 'fiddle'
6
6
  require 'fiddle/import'
7
7
 
8
8
  # FasterPath module behaves as a singleton object with the alternative method
9
- # implementations for Pathname, and some for File, available directly on it.
9
+ # implementations for `Pathname`, and some for `File`, available directly on it.
10
10
  #
11
+ # New projects are recommend to reference methods defined directly on `FasterPath`.
12
+ # Existing websites may use the `FasterPath.sledgehammer_everything!` method to
13
+ # directly injet the more performant implementations of path handling in to their
14
+ # existing code ecosystem. To do so you will need to
15
+ # `require 'faster_path/optional/monkeypatches'` beforehand.
11
16
  module FasterPath
12
17
  FFI_LIBRARY = begin
13
18
  toplevel_dir = File.dirname(__dir__)
@@ -1,3 +1,3 @@
1
1
  module FasterPath
2
- VERSION = "0.3.9"
2
+ VERSION = "0.3.10"
3
3
  end
@@ -9,7 +9,6 @@ use plus;
9
9
  use relative_path_from;
10
10
  use debug;
11
11
  use helpers::{TryFrom, to_str};
12
- use pathname_sys::null_byte_check;
13
12
  use path_parsing::{SEP, find_last_non_sep_pos};
14
13
 
15
14
  use ruru;
@@ -23,10 +22,11 @@ use ruru::{
23
22
  Class,
24
23
  VerifiedObject,
25
24
  Exception as Exc,
26
- AnyException as Exception
25
+ AnyException as Exception,
27
26
  };
28
27
  use ruru::types::{Value, ValueType};
29
- use std::path::{MAIN_SEPARATOR,Path};
28
+ use std::borrow::Cow;
29
+ use std::path::{MAIN_SEPARATOR, Path};
30
30
  use std::fs;
31
31
 
32
32
  type MaybeArray = Result<ruru::Array, ruru::result::Error>;
@@ -45,31 +45,6 @@ impl Pathname {
45
45
  Pathname { value: instance.value() }
46
46
  }
47
47
 
48
- pub fn new_checked(path: AnyObject) -> Result<Pathname, Exception> {
49
- let pth: Value = if Class::from_existing("String").case_equals(&path) {
50
- path.value()
51
- } else if path.respond_to("to_path") {
52
- path.send("to_path", None).value()
53
- } else {
54
- return Err(
55
- Exception::new(
56
- "ArgumentError",
57
- Some("The type for the argument provided to Pathname.new was invalid.")
58
- )
59
- )
60
- };
61
-
62
- if null_byte_check(path.value()) {
63
- return Err( Exception::new("ArgumentError", Some("pathname contains null byte")) )
64
- }
65
-
66
- // if it crashes then dup the path string here before assigning to @path
67
- let mut instance = Class::from_existing("Pathname").allocate();
68
- instance.instance_variable_set("@path", RString::from(pth).to_any_object());
69
-
70
- Ok(Pathname { value: instance.value() })
71
- }
72
-
73
48
  pub fn to_any_object(&self) -> AnyObject {
74
49
  AnyObject::from(self.value())
75
50
  }
@@ -77,7 +52,7 @@ impl Pathname {
77
52
 
78
53
  impl From<Value> for Pathname {
79
54
  fn from(value: Value) -> Self {
80
- Pathname { value: value }
55
+ Pathname { value }
81
56
  }
82
57
  }
83
58
 
@@ -302,29 +277,15 @@ pub fn pn_has_trailing_separator(pth: MaybeString) -> Boolean {
302
277
  }
303
278
 
304
279
  pub fn pn_join(args: MaybeArray) -> AnyObject {
305
- let mut args = args.unwrap();
306
- let path_self = anyobject_to_string(args.shift()).unwrap();
307
- let mut qty = args.length();
308
- if qty <= 0 {
309
- return Pathname::new(&path_self).to_any_object();
310
- }
311
-
312
- let mut result = String::new();
313
-
314
- loop {
315
- if qty == 0 { break; }
316
-
317
- let item = args.pop();
318
- result = plus::plus_paths(&anyobject_to_string(item).unwrap(), &result);
319
- if result.as_bytes().get(0) == Some(&SEP) {
320
- return Pathname::new(&result).to_any_object()
280
+ let paths = args.unwrap().into_iter().map(|arg| anyobject_to_string(arg).unwrap()).collect::<Vec<_>>();
281
+ let mut paths_iter = paths.iter().rev();
282
+ let mut result = Cow::Borrowed(paths_iter.next().unwrap().as_str());
283
+ for part in paths_iter {
284
+ result = plus::plus_paths(&part, result.as_ref());
285
+ if result.as_bytes().first() == Some(&SEP) {
286
+ break;
321
287
  }
322
-
323
- qty -= 1;
324
288
  }
325
-
326
- let result = plus::plus_paths(&path_self, &result);
327
-
328
289
  Pathname::new(&result).to_any_object()
329
290
  }
330
291
 
@@ -2,23 +2,9 @@ use ruru::{AnyObject, Array, Object, AnyException};
2
2
  use ruru::types::{Argc, Value, CallbackPtr};
3
3
  use ruru::util::str_to_cstring;
4
4
  extern crate ruby_sys;
5
- use self::ruby_sys::{class, util, vm, string};
5
+ use self::ruby_sys::{class, util, vm};
6
6
  use ::pathname;
7
7
  extern crate memchr;
8
- use self::memchr::memchr;
9
- use std::slice;
10
-
11
- pub fn null_byte_check(value: Value) -> bool {
12
- unsafe {
13
- let str = string::rb_string_value_ptr(&value) as *const u8;
14
-
15
- // `rb_str_len` is a ruby_sys specific thing. Consider:
16
- // extern { fn rb_str_strlen(value: Value) -> c_long } as isize
17
- let len = string::rb_str_len(value) as usize;
18
-
19
- memchr(b'\0', slice::from_raw_parts(str, len)).is_some()
20
- }
21
- }
22
8
 
23
9
  pub fn raise(exception: AnyException) {
24
10
  unsafe { vm::rb_exc_raise(exception.value()); }
@@ -1,83 +1,80 @@
1
- extern crate array_tool;
2
- use std::path::Path;
1
+ use std::borrow::Cow;
3
2
  use std::str;
3
+ use std::path::MAIN_SEPARATOR;
4
+
4
5
  use chop_basename::chop_basename;
5
- use basename::basename;
6
- use dirname::dirname;
7
- use self::array_tool::vec::Shift;
8
- use std::ops::Index;
6
+ use path_parsing::SEP;
9
7
 
10
- pub fn plus_paths(path1: &str, path2: &str) -> String {
11
- let mut prefix2 = path2.to_string();
8
+ pub fn plus_paths<'a>(path1: &'a str, path2: &str) -> Cow<'a, str> {
9
+ let mut prefix2 = path2;
12
10
  let mut index_list2: Vec<usize> = vec![];
13
- let mut basename_list2: Vec<String> = vec![];
14
-
11
+ let mut basename_list2: Vec<&str> = vec![];
15
12
  loop {
16
- match chop_basename(&prefix2.clone()[..]) {
17
- None => { break },
13
+ match chop_basename(prefix2) {
14
+ None => { break; }
18
15
  Some((pfx2, basename2)) => {
19
- prefix2 = pfx2.to_string();
20
- index_list2.unshift(pfx2.len());
21
- basename_list2.unshift(basename2.to_owned());
22
- },
16
+ prefix2 = pfx2;
17
+ index_list2.push(pfx2.len());
18
+ basename_list2.push(basename2);
19
+ }
23
20
  }
24
21
  }
25
22
  if !prefix2.is_empty() {
26
- return path2.to_string()
23
+ return path2.to_string().into();
27
24
  };
28
25
 
29
- let mut prefix1 = path1.to_string();
30
-
26
+ let result_prefix: Cow<str>;
27
+ let mut prefix1 = path1;
31
28
  loop {
32
- while !basename_list2.is_empty() && basename_list2.first().unwrap() == "." {
33
- index_list2.shift();
34
- basename_list2.shift();
35
- }
36
- match chop_basename(&prefix1.clone()[..]) {
37
- None => { break },
29
+ let mut new_len = basename_list2.len() - count_trailing(".", &basename_list2);
30
+ index_list2.truncate(new_len);
31
+ basename_list2.truncate(new_len);
32
+ match chop_basename(prefix1) {
33
+ None => {
34
+ result_prefix = prefix1.into();
35
+ break;
36
+ }
38
37
  Some((pfx1, basename1)) => {
39
- prefix1 = pfx1.to_string();
40
- if basename1 == "." { continue };
41
- if basename1 == ".." || basename_list2.is_empty() || basename_list2.first().unwrap() != ".." {
42
- prefix1.push_str(&basename1);
43
- break
44
- }
38
+ prefix1 = pfx1;
39
+ if basename1 == "." { continue; };
40
+ if basename1 == ".." || basename_list2.last() != Some(&"..") {
41
+ result_prefix = [prefix1, basename1].concat().into();
42
+ break;
45
43
  }
44
+ }
45
+ }
46
+ if new_len > 0 {
47
+ new_len -= 1;
48
+ index_list2.truncate(new_len);
49
+ basename_list2.truncate(new_len);
46
50
  }
47
- index_list2.shift();
48
- basename_list2.shift();
49
51
  }
50
52
 
51
- let result: String;
52
-
53
- let mut r1 = if let Some((_,_)) = chop_basename(&prefix1[..]) {true} else {false};
54
-
55
- if !r1 {
56
- r1 = basename(&prefix1[..], "").contains("/");
57
- if r1 {
58
- while !basename_list2.is_empty() && basename_list2.first().unwrap() == ".." {
59
- index_list2.shift();
60
- basename_list2.shift();
61
- }
62
- }
53
+ if !result_prefix.is_empty() && result_prefix.as_bytes().iter().cloned().all(|b| b == SEP) {
54
+ let new_len = basename_list2.len() - count_trailing("..", &basename_list2);
55
+ index_list2.truncate(new_len);
56
+ basename_list2.truncate(new_len);
63
57
  }
64
- if !basename_list2.is_empty() {
65
- let suffix2 = path2.index(index_list2.first().unwrap().to_owned()..);
66
- if r1 {
67
- result = Path::new(&prefix1).join(Path::new(&suffix2)).to_str().unwrap().to_string();
68
- } else {
69
- prefix1.push_str(&suffix2);
70
- result = prefix1.to_string();
58
+ if let Some(last_index2) = index_list2.last() {
59
+ let suffix = &path2[*last_index2..];
60
+ match (result_prefix.as_bytes().last(), suffix.as_bytes().first()) {
61
+ (Some(&SEP), Some(&SEP)) => [&result_prefix, &suffix[1..]].concat().into(),
62
+ (Some(&SEP), Some(_)) | (Some(_), Some(&SEP)) => [&result_prefix, suffix].concat().into(),
63
+ (None, Some(_)) => suffix.to_string().into(),
64
+ _ => format!("{}{}{}", result_prefix.as_ref(), MAIN_SEPARATOR, suffix).into(),
71
65
  }
72
66
  } else {
73
- if r1 {
74
- result = prefix1.to_string();
67
+ if result_prefix.is_empty() {
68
+ ".".into()
75
69
  } else {
76
- result = dirname(&prefix1[..]).to_string();
70
+ result_prefix
77
71
  }
78
72
  }
73
+ }
79
74
 
80
- String::from(result)
75
+ #[inline(always)]
76
+ fn count_trailing(x: &str, xs: &Vec<&str>) -> usize {
77
+ xs.iter().rev().take_while(|&c| c == &x).count()
81
78
  }
82
79
 
83
80
  #[test]
@@ -90,6 +87,7 @@ fn it_will_plus_same_as_ruby() {
90
87
  assert_eq!("/b" , plus_paths("a" , "/b"));
91
88
 
92
89
  assert_eq!("/" , plus_paths("/" , ".."));
90
+ assert_eq!("////" , plus_paths("////", ""));
93
91
  assert_eq!("." , plus_paths("a" , ".."));
94
92
  assert_eq!("a" , plus_paths("a/b", ".."));
95
93
  assert_eq!("../.." , plus_paths(".." , ".."));
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.9
4
+ version: 0.3.10
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-14 00:00:00.000000000 Z
11
+ date: 2018-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '12.0'
33
+ version: '12.3'
34
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: '12.0'
40
+ version: '12.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: thermite
43
43
  requirement: !ruby/object:Gem::Requirement