Skip to content

Commit 336ce72

Browse files
authored
Merge pull request #138 from danielpclark/del_trailing_separator
Del trailing separator
2 parents 664a4ea + 035b072 commit 336ce72

10 files changed

+184
-26
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,15 @@ improvement result for the `chop_basename` method.
136136

137137
Current methods implemented:
138138

139-
|FasterPath Rust Implementation|Ruby 2.3.4 Implementation|Time Shaved Off|
139+
|FasterPath Rust Implementation|Ruby 2.5.0 Implementation|Time Shaved Off|
140140
|---|---|:---:|
141141
| `FasterPath.absolute?` | `Pathname#absolute?` | 91.9% |
142142
| `FasterPath.add_trailing_separator` | `Pathname#add_trailing_separator` | 31.2% |
143143
| `FasterPath.children` | `Pathname#children` | 13.2% |
144144
| `FasterPath.chop_basename` | `Pathname#chop_basename` | 54.5% |
145145
| `FasterPath.cleanpath_aggressive` | `Pathname#cleanpath_aggressive` | 73.8% |
146146
| `FasterPath.cleanpath_conservative` | `Pathname#cleanpath_conservative` | 70.7% |
147+
| `FasterPath.del_trailing_separator` | `Pathname#del_trailing_separator` | 80.6% |
147148
| `FasterPath.directory?` | `Pathname#directory?` | 11.3% |
148149
| `FasterPath.entries` | `Pathname#entries` | 8.4% |
149150
| `FasterPath.has_trailing_separator?` | `Pathname#has_trailing_separator` | 67.6% |

lib/faster_path/optional/monkeypatches.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ def cleanpath_conservative
5757
end
5858
private :cleanpath_conservative
5959

60+
def del_trailing_separator(pth)
61+
FasterPath.del_trailing_separator(pth)
62+
end
63+
private :del_trailing_separator
64+
6065
def directory?
6166
FasterPath.directory?(@path)
6267
end

lib/faster_path/optional/refinements.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ def cleanpath_conservative
5353
end
5454
private :cleanpath_conservative
5555

56+
def del_trailing_separator(pth)
57+
FasterPath.del_trailing_separator(pth)
58+
end
59+
private :del_trailing_separator
60+
5661
def directory?
5762
FasterPath.directory?(@path)
5863
end

src/helpers.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
use ruru::{RString, Object, Class, AnyObject};
2-
3-
#[inline]
4-
pub fn new_pathname_instance(path: &str) -> Class {
5-
let mut instance = Class::from_existing("Pathname").allocate();
6-
instance.instance_variable_set("@path", RString::new(path).to_any_object());
7-
8-
instance
9-
}
2+
use pathname::Pathname;
103

114
pub fn anyobject_to_string(item: AnyObject) -> Result<String, RubyDebugInfo> {
125
let result = &item;
@@ -35,7 +28,7 @@ pub fn anyobject_to_string(item: AnyObject) -> Result<String, RubyDebugInfo> {
3528
#[allow(dead_code)]
3629
pub fn into_pathname(itself: AnyObject) -> Result<AnyObject, RubyDebugInfo> {
3730
if Class::from_existing("String").case_equals(&itself) {
38-
Ok(new_pathname_instance(
31+
Ok(Pathname::new(
3932
&RString::from(itself.value()).to_string()
4033
).to_any_object())
4134
} else if Class::from_existing("Pathname").case_equals(&itself) {

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ methods!(
7171
pathname::pn_cleanpath_conservative(pth)
7272
}
7373

74-
// fn r_del_trailing_separator(pth: RString){}
74+
fn pub_del_trailing_separator(pth: RString) -> RString {
75+
pathname::pn_del_trailing_separator(pth)
76+
}
7577

7678
// fn r_descend(){}
7779

@@ -151,6 +153,7 @@ pub extern "C" fn Init_faster_pathname(){
151153
Module::from_existing("FasterPath").define(|itself| {
152154
itself.def_self("absolute?", pub_is_absolute);
153155
itself.def_self("add_trailing_separator", pub_add_trailing_separator);
156+
itself.def_self("del_trailing_separator", pub_del_trailing_separator);
154157
itself.def_self("cleanpath_aggressive", pub_cleanpath_aggressive);
155158
itself.def_self("cleanpath_conservative", pub_cleanpath_conservative);
156159
itself.def_self("directory?", pub_is_directory);

src/pathname.rs

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,56 @@ use extname;
88
use plus;
99

1010
use ruru;
11-
use ruru::{RString, Boolean, Array, AnyObject, NilClass, Object};
11+
use ruru::{RString, Boolean, Array, AnyObject, NilClass, Object, Class, VerifiedObject};
12+
use ruru::types::{Value, ValueType};
1213
use std::path::{MAIN_SEPARATOR,Path};
1314
use std::fs;
1415

1516
type MaybeArray = Result<ruru::Array, ruru::result::Error>;
1617
type MaybeString = Result<ruru::RString, ruru::result::Error>;
1718
type MaybeBoolean = Result<ruru::Boolean, ruru::result::Error>;
1819

20+
pub struct Pathname {
21+
value: Value
22+
}
23+
24+
impl Pathname {
25+
pub fn new(path: &str) -> Pathname {
26+
let mut instance = Class::from_existing("Pathname").allocate();
27+
instance.instance_variable_set("@path", RString::new(path).to_any_object());
28+
29+
Pathname { value: instance.value() }
30+
}
31+
32+
pub fn to_any_object(&self) -> AnyObject {
33+
AnyObject::from(self.value())
34+
}
35+
}
36+
37+
impl From<Value> for Pathname {
38+
fn from(value: Value) -> Self {
39+
Pathname { value: value }
40+
}
41+
}
42+
43+
impl Object for Pathname {
44+
#[inline]
45+
fn value(&self) -> Value {
46+
self.value
47+
}
48+
}
49+
50+
impl VerifiedObject for Pathname {
51+
fn is_correct_type<T: Object>(object: &T) -> bool {
52+
object.value().ty() == ValueType::Class &&
53+
Class::from_existing("Pathname").case_equals(object)
54+
}
55+
56+
fn error_message() -> &'static str {
57+
"Error converting to Pathname"
58+
}
59+
}
60+
1961
pub fn pn_add_trailing_separator(pth: MaybeString) -> RString {
2062
let p = pth.ok().unwrap();
2163
let x = format!("{}{}", p.to_str(), "a");
@@ -90,11 +132,11 @@ pub fn pn_children_compat(pth: MaybeString, with_dir: MaybeBoolean) -> AnyObject
90132
for entry in entries {
91133
if with_directory {
92134
if let Ok(v) = entry {
93-
arr.push(new_pathname_instance(v.path().to_str().unwrap()));
135+
arr.push(Pathname::new(v.path().to_str().unwrap()));
94136
};
95137
} else {
96138
if let Ok(v) = entry {
97-
arr.push(new_pathname_instance(v.file_name().to_str().unwrap()));
139+
arr.push(Pathname::new(v.file_name().to_str().unwrap()));
98140
};
99141
}
100142
}
@@ -138,7 +180,25 @@ pub fn pn_cleanpath_conservative(pth: MaybeString) -> RString {
138180
RString::new(&path)
139181
}
140182

141-
// pub fn pn_del_trailing_separator(pth: MaybeString){}
183+
pub fn pn_del_trailing_separator(pth: MaybeString) -> RString {
184+
if let &Ok(ref path) = &pth {
185+
let path = path.to_str();
186+
187+
if !path.is_empty() {
188+
let path = path.trim_right_matches('/');
189+
190+
if path.is_empty() {
191+
return RString::new("/");
192+
} else {
193+
return RString::new(path);
194+
}
195+
}
196+
} else {
197+
return RString::new("");
198+
}
199+
200+
pth.unwrap()
201+
}
142202

143203
// pub fn pn_descend(){}
144204

@@ -188,12 +248,12 @@ pub fn pn_entries_compat(pth: MaybeString) -> AnyObject {
188248
if let Ok(files) = fs::read_dir(pth.ok().unwrap_or(RString::new("")).to_str()) {
189249
let mut arr = Array::new();
190250

191-
arr.push(new_pathname_instance("."));
192-
arr.push(new_pathname_instance(".."));
251+
arr.push(Pathname::new("."));
252+
arr.push(Pathname::new(".."));
193253

194254
for file in files {
195255
let file_name_str = file.unwrap().file_name().into_string().unwrap();
196-
arr.push(new_pathname_instance(&file_name_str));
256+
arr.push(Pathname::new(&file_name_str));
197257
}
198258

199259
arr.to_any_object()
@@ -227,7 +287,7 @@ pub fn pn_join(args: MaybeArray) -> AnyObject {
227287
let path_self = anyobject_to_string(args.shift()).unwrap();
228288
let mut qty = args.length();
229289
if qty <= 0 {
230-
return new_pathname_instance(&path_self).to_any_object();
290+
return Pathname::new(&path_self).to_any_object();
231291
}
232292

233293
let mut result = String::new();
@@ -238,15 +298,15 @@ pub fn pn_join(args: MaybeArray) -> AnyObject {
238298
let item = args.pop();
239299
result = plus::plus_paths(&anyobject_to_string(item).unwrap(), &result);
240300
if result.chars().next() == Some(MAIN_SEPARATOR) {
241-
return new_pathname_instance(&result).to_any_object()
301+
return Pathname::new(&result).to_any_object()
242302
}
243303

244304
qty -= 1;
245305
}
246306

247307
let result = plus::plus_paths(&path_self, &result);
248308

249-
new_pathname_instance(&result).to_any_object()
309+
Pathname::new(&result).to_any_object()
250310
}
251311

252312
// pub fn pn_mkpath(pth: MaybeString) -> NilClass {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
require 'benchmark_helper'
2+
3+
class DelTrailingSeparatorBenchmark < BenchmarkHelper
4+
def setup
5+
@file ||= __FILE__
6+
end
7+
8+
def teardown
9+
super
10+
graph_benchmarks
11+
end
12+
13+
def bench_rust_del_trailing_separator
14+
benchmark :rust do
15+
FasterPath.del_trailing_separator('/hello/world')
16+
FasterPath.del_trailing_separator('/hello/world/')
17+
end
18+
end
19+
20+
def bench_ruby_del_trailing_separator
21+
benchmark :ruby do
22+
Pathname.allocate.send(:del_trailing_separator, '/hello/world')
23+
Pathname.allocate.send(:del_trailing_separator, '/hello/world/')
24+
end
25+
end
26+
27+
end

test/benches/join_benchmark.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
class JoinBenchmark < BenchmarkHelper
44
def setup
55
@file ||= __FILE__
6+
@pathname = Pathname.new(".").freeze
67
end
78

89
def teardown
@@ -12,17 +13,15 @@ def teardown
1213

1314
def bench_rust_join
1415
benchmark :rust do
15-
FasterPath.join('a', 'b')
1616
FasterPath.join('.', 'b')
17-
FasterPath.join('a//b/c', '../d//e')
17+
FasterPath.join('.', '../d//e')
1818
end
1919
end
2020

2121
def bench_ruby_join
2222
benchmark :ruby do
23-
Pathname.allocate.send(:join, 'a', 'b')
24-
Pathname.allocate.send(:join, '.', 'b')
25-
Pathname.allocate.send(:join, 'a//b/c', '../d//e')
23+
@pathname.send(:join, 'b')
24+
@pathname.send(:join, '../d//e')
2625
end
2726
end
2827
end

test/del_trailing_separator_test.rb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
require 'test_helper'
2+
3+
class DelTrailingSeparatorTest < Minitest::Test
4+
def test_del_trailing_separator
5+
assert_equal FasterPath.del_trailing_separator(""), ""
6+
assert_equal FasterPath.del_trailing_separator("/"), "/"
7+
assert_equal FasterPath.del_trailing_separator("/a"), "/a"
8+
assert_equal FasterPath.del_trailing_separator("/a/"), "/a"
9+
assert_equal FasterPath.del_trailing_separator("/a//"), "/a"
10+
assert_equal FasterPath.del_trailing_separator("."), "."
11+
assert_equal FasterPath.del_trailing_separator("./"), "."
12+
assert_equal FasterPath.del_trailing_separator(".//"), "."
13+
end
14+
15+
if File.dirname("A:") == "A:."
16+
def test_del_trailing_separator_dos_drive_letter
17+
assert_equal FasterPath.del_trailing_separator("A:"), "A:"
18+
assert_equal FasterPath.del_trailing_separator("A:/"), "A:/"
19+
assert_equal FasterPath.del_trailing_separator("A://"), "A:/"
20+
assert_equal FasterPath.del_trailing_separator("A:."), "A:."
21+
assert_equal FasterPath.del_trailing_separator("A:./"), "A:."
22+
assert_equal FasterPath.del_trailing_separator("A:.//"), "A:."
23+
end
24+
end
25+
26+
def test_del_trailing_separator_dos_unc
27+
if File.dirname("//") == "//"
28+
assert_equal FasterPath.del_trailing_separator("//"), "//"
29+
assert_equal FasterPath.del_trailing_separator("//a"), "//a"
30+
assert_equal FasterPath.del_trailing_separator("//a/"), "//a"
31+
assert_equal FasterPath.del_trailing_separator("//a//"), "//a"
32+
assert_equal FasterPath.del_trailing_separator("//a/b"), "//a/b"
33+
assert_equal FasterPath.del_trailing_separator("//a/b/"), "//a/b"
34+
assert_equal FasterPath.del_trailing_separator("//a/b//"), "//a/b"
35+
assert_equal FasterPath.del_trailing_separator("//a/b/c"), "//a/b/c"
36+
assert_equal FasterPath.del_trailing_separator("//a/b/c/"), "//a/b/c"
37+
assert_equal FasterPath.del_trailing_separator("//a/b/c//"), "//a/b/c"
38+
else
39+
assert_equal FasterPath.del_trailing_separator("///"), "/"
40+
assert_equal FasterPath.del_trailing_separator("///a/"), "///a"
41+
end
42+
end
43+
44+
if !File::ALT_SEPARATOR.nil?
45+
def test_del_trailing_separator_dos
46+
assert_equal FasterPath.del_trailing_separator("a\\"), "a"
47+
assert_equal FasterPath.del_trailing_separator("\225\\\\".dup.force_encoding("cp932")), "\225\\".dup.force_encoding("cp932")
48+
assert_equal FasterPath.del_trailing_separator("\225\\\\".dup.force_encoding("cp437")), "\225".dup.force_encoding("cp437")
49+
end
50+
end
51+
end

test/pbench/pbench_suite.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,20 @@
150150
end
151151
end
152152
}
153+
PBENCHES[:del_trailing_separator] = {
154+
new: lambda do |x|
155+
x.times do
156+
FasterPath.del_trailing_separator('/hello/world')
157+
FasterPath.del_trailing_separator('/hello/world/')
158+
end
159+
end,
160+
old: lambda do |x|
161+
x.times do
162+
PATHNAME_DOT.send(:del_trailing_separator, '/hello/world')
163+
PATHNAME_DOT.send(:del_trailing_separator, '/hello/world/')
164+
end
165+
end
166+
}
153167
PBENCHES[:"directory?"] = {
154168
new: lambda do |x|
155169
x.times do

0 commit comments

Comments
 (0)