faster_path 0.3.9 → 0.3.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
  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