Skip to content

Commit fcc1fa0

Browse files
authored
Merge pull request #159 from glebm/various-perf
Various performance improvements to pathname
2 parents 0257714 + f3d6f1e commit fcc1fa0

File tree

3 files changed

+83
-94
lines changed

3 files changed

+83
-94
lines changed

src/helpers.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use ruru::{RString, Object, Class, AnyObject};
22
extern crate ruby_sys;
33
use debug::RubyDebugInfo;
4+
use ruru;
5+
6+
type MaybeString = Result<ruru::RString, ruru::result::Error>;
47

58
pub trait TryFrom<T>: Sized {
69
type Error;
@@ -22,17 +25,24 @@ pub fn anyobject_to_string(item: AnyObject) -> Result<String, RubyDebugInfo> {
2225
if Class::from_existing("String").case_equals(result) {
2326
return Ok(RString::from(result.value()).to_string())
2427
}
25-
28+
2629
if Class::from_existing("Pathname").case_equals(result) {
2730
return Ok(result.instance_variable_get("@path").
2831
try_convert_to::<RString>().
2932
unwrap_or(RString::new("")).
3033
to_string())
3134
}
32-
35+
3336
if result.respond_to("to_path") {
3437
return Ok(RString::from(result.send("to_path", None).value()).to_string())
3538
}
3639

3740
Ok(RString::from(result.send("to_s", None).value()).to_string())
3841
}
42+
43+
pub fn to_str(maybe_string: &MaybeString) -> &str {
44+
match maybe_string {
45+
&Ok(ref ruru_string) => ruru_string.to_str(),
46+
&Err(_) => "",
47+
}
48+
}

src/pathname.rs

+54-77
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use extname;
88
use plus;
99
use relative_path_from;
1010
use debug;
11-
use helpers::TryFrom;
11+
use helpers::{TryFrom, to_str};
1212
use pathname_sys::null_byte_check;
13+
use path_parsing::{SEP, find_last_non_sep_pos};
1314

1415
use ruru;
1516
use ruru::{
@@ -84,11 +85,11 @@ impl TryFrom<AnyObject> for Pathname {
8485
type Error = debug::RubyDebugInfo;
8586
fn try_from(obj: AnyObject) -> Result<Pathname, Self::Error> {
8687
if Class::from_existing("String").case_equals(&obj) {
87-
Ok(Pathname::new(&RString::from(obj.value()).to_string()))
88+
Ok(Pathname::new(&RString::from(obj.value()).to_str()))
8889
} else if Class::from_existing("Pathname").case_equals(&obj) {
8990
Ok(Pathname::from(obj.value()))
9091
} else if obj.respond_to("to_path") {
91-
Ok(Pathname::new(&RString::from(obj.send("to_path", None).value()).to_string()))
92+
Ok(Pathname::new(&RString::from(obj.send("to_path", None).value()).to_str()))
9293
} else {
9394
Err(Self::Error::from(obj))
9495
}
@@ -113,15 +114,8 @@ impl VerifiedObject for Pathname {
113114
}
114115
}
115116

116-
fn to_str(maybe_string: &MaybeString) -> &str {
117-
match maybe_string {
118-
&Ok(ref ruru_string) => ruru_string.to_str(),
119-
&Err(_) => "",
120-
}
121-
}
122-
123117
pub fn pn_add_trailing_separator(pth: MaybeString) -> RString {
124-
let p = pth.ok().unwrap();
118+
let p = pth.unwrap();
125119
let x = format!("{}{}", p.to_str(), "a");
126120
match x.rsplit_terminator(MAIN_SEPARATOR).next() {
127121
Some("a") => p,
@@ -130,10 +124,7 @@ pub fn pn_add_trailing_separator(pth: MaybeString) -> RString {
130124
}
131125

132126
pub fn pn_is_absolute(pth: MaybeString) -> Boolean {
133-
Boolean::new(match to_str(&pth).chars().next() {
134-
Some(c) => c == MAIN_SEPARATOR,
135-
None => false
136-
})
127+
Boolean::new(to_str(&pth).as_bytes().get(0) == Some(&SEP))
137128
}
138129

139130
// pub fn pn_ascend(){}
@@ -143,16 +134,16 @@ pub fn pn_basename(pth: MaybeString, ext: MaybeString) -> RString {
143134
}
144135

145136
pub fn pn_children(pth: MaybeString, with_dir: MaybeBoolean) -> Result<AnyObject, Exception> {
146-
let val = pth.ok().unwrap_or(RString::new("."));
147-
let val = val.to_str();
137+
let path = pth.unwrap_or(RString::new("."));
138+
let path = path.to_str();
148139

149-
if let Ok(entries) = fs::read_dir(val) {
150-
let mut with_directory = with_dir.ok().unwrap_or(Boolean::new(true)).to_bool();
151-
if val == "." {
140+
if let Ok(entries) = fs::read_dir(path) {
141+
let mut with_directory = with_dir.unwrap_or(Boolean::new(true)).to_bool();
142+
if path == "." {
152143
with_directory = false;
153144
}
154145

155-
let mut arr = Array::new();
146+
let mut arr = Array::with_capacity(entries.size_hint().1.unwrap_or(0));
156147
for entry in entries {
157148
if with_directory {
158149
match entry {
@@ -169,21 +160,21 @@ pub fn pn_children(pth: MaybeString, with_dir: MaybeBoolean) -> Result<AnyObject
169160

170161
Ok(arr.to_any_object())
171162
} else {
172-
let msg = format!("No such file or directory @ dir_initialize - {}", val);
163+
let msg = format!("No such file or directory @ dir_initialize - {}", path);
173164
Err(Exception::new("Errno::NOENT", Some(&msg)))
174165
}
175166
}
176167

177168
pub fn pn_children_compat(pth: MaybeString, with_dir: MaybeBoolean) -> Result<AnyObject, Exception> {
178-
let val = to_str(&pth);
169+
let path = to_str(&pth);
179170

180-
if let Ok(entries) = fs::read_dir(val) {
181-
let mut with_directory = with_dir.ok().unwrap_or(Boolean::new(true)).to_bool();
182-
if val == "." {
171+
if let Ok(entries) = fs::read_dir(path) {
172+
let mut with_directory = with_dir.unwrap_or(Boolean::new(true)).to_bool();
173+
if path == "." {
183174
with_directory = false;
184175
}
185176

186-
let mut arr = Array::new();
177+
let mut arr = Array::with_capacity(entries.size_hint().1.unwrap_or(0));
187178
for entry in entries {
188179
if with_directory {
189180
if let Ok(v) = entry {
@@ -198,18 +189,17 @@ pub fn pn_children_compat(pth: MaybeString, with_dir: MaybeBoolean) -> Result<An
198189

199190
Ok(arr.to_any_object())
200191
} else {
201-
let msg = format!("No such file or directory @ dir_initialize - {}", val);
192+
let msg = format!("No such file or directory @ dir_initialize - {}", path);
202193
Err(Exception::new("Errno::NOENT", Some(&msg)))
203194
}
204195
}
205196

206197
pub fn pn_chop_basename(pth: MaybeString) -> AnyObject {
207-
let mut arr = Array::with_capacity(2);
208-
let results = chop_basename::chop_basename(to_str(&pth));
209-
match results {
198+
match chop_basename::chop_basename(to_str(&pth)) {
210199
Some((dirname, basename)) => {
211-
arr.push(RString::new(&dirname[..]));
212-
arr.push(RString::new(&basename[..]));
200+
let mut arr = Array::with_capacity(2);
201+
arr.push(RString::new(&dirname));
202+
arr.push(RString::new(&basename));
213203
arr.to_any_object()
214204
},
215205
None => NilClass::new().to_any_object()
@@ -227,22 +217,19 @@ pub fn pn_cleanpath_conservative(pth: MaybeString) -> RString {
227217
}
228218

229219
pub fn pn_del_trailing_separator(pth: MaybeString) -> RString {
230-
if let &Ok(ref path) = &pth {
231-
let path = path.to_str();
232-
233-
if !path.is_empty() {
234-
let path = path.trim_right_matches('/');
235-
236-
if path.is_empty() {
237-
return RString::new("/");
238-
} else {
239-
return RString::new(path);
240-
}
220+
{
221+
let path = to_str(&pth);
222+
if path.is_empty() {
223+
return RString::new("/");
224+
}
225+
let pos = match find_last_non_sep_pos(path.as_bytes()) {
226+
Some(pos) => pos,
227+
None => return RString::new("/"),
228+
};
229+
if pos != path.len() - 1 {
230+
return RString::new(&path[..pos + 1]);
241231
}
242-
} else {
243-
return RString::new("");
244232
}
245-
246233
pth.unwrap()
247234
}
248235

@@ -263,41 +250,39 @@ pub fn pn_dirname(pth: MaybeString) -> RString {
263250
// }
264251

265252
pub fn pn_entries(pth: MaybeString) -> Result<AnyObject, Exception> {
266-
if let Ok(files) = fs::read_dir(to_str(&pth)) {
267-
let mut arr = Array::new();
253+
let path = to_str(&pth);
254+
if let Ok(files) = fs::read_dir(path) {
255+
let mut arr = Array::with_capacity(files.size_hint().1.unwrap_or(0) + 2);
268256

269257
arr.push(RString::new("."));
270258
arr.push(RString::new(".."));
271259

272260
for file in files {
273-
let file_name_str = file.unwrap().file_name().into_string().unwrap();
274-
arr.push(RString::new(&file_name_str[..]));
261+
arr.push(RString::new(file.unwrap().file_name().to_str().unwrap()));
275262
}
276263

277264
Ok(arr.to_any_object())
278265
} else {
279-
let val = pth.unwrap_or(RString::new(""));
280-
let msg = format!("No such file or directory @ dir_initialize - {}", val.to_str());
266+
let msg = format!("No such file or directory @ dir_initialize - {}", path);
281267
Err(Exception::new("Errno::NOENT", Some(&msg)))
282268
}
283269
}
284270

285271
pub fn pn_entries_compat(pth: MaybeString) -> Result<AnyObject, Exception> {
286-
if let Ok(files) = fs::read_dir(to_str(&pth)) {
287-
let mut arr = Array::new();
272+
let path = to_str(&pth);
273+
if let Ok(files) = fs::read_dir(path) {
274+
let mut arr = Array::with_capacity(files.size_hint().1.unwrap_or(0) + 2);
288275

289276
arr.push(Pathname::new("."));
290277
arr.push(Pathname::new(".."));
291278

292279
for file in files {
293-
let file_name_str = file.unwrap().file_name().into_string().unwrap();
294-
arr.push(Pathname::new(&file_name_str));
280+
arr.push(Pathname::new(file.unwrap().file_name().to_str().unwrap()));
295281
}
296282

297283
Ok(arr.to_any_object())
298284
} else {
299-
let val = pth.unwrap_or(RString::new(""));
300-
let msg = format!("No such file or directory @ dir_initialize - {}", val.to_str());
285+
let msg = format!("No such file or directory @ dir_initialize - {}", path);
301286
Err(Exception::new("Errno::NOENT", Some(&msg)))
302287
}
303288
}
@@ -309,11 +294,9 @@ pub fn pn_extname(pth: MaybeString) -> RString {
309294
// pub fn pn_find(pth: MaybeString, ignore_error: Boolean){}
310295

311296
pub fn pn_has_trailing_separator(pth: MaybeString) -> Boolean {
312-
let v = pth.ok().unwrap_or(RString::new(""));
313-
match chop_basename::chop_basename(v.to_str()) {
314-
Some((a,b)) => {
315-
Boolean::new(a.len() + b.len() < v.to_str().len())
316-
},
297+
let v = to_str(&pth);
298+
match chop_basename::chop_basename(v) {
299+
Some((a,b)) => Boolean::new(a.len() + b.len() < v.len()),
317300
_ => Boolean::new(false)
318301
}
319302
}
@@ -333,7 +316,7 @@ pub fn pn_join(args: MaybeArray) -> AnyObject {
333316

334317
let item = args.pop();
335318
result = plus::plus_paths(&anyobject_to_string(item).unwrap(), &result);
336-
if result.chars().next() == Some(MAIN_SEPARATOR) {
319+
if result.as_bytes().get(0) == Some(&SEP) {
337320
return Pathname::new(&result).to_any_object()
338321
}
339322

@@ -354,23 +337,17 @@ pub fn pn_join(args: MaybeArray) -> AnyObject {
354337
// pub fn pn_parent(pth: MaybeString){}
355338

356339
pub fn pn_plus(pth1: MaybeString, pth2: MaybeString) -> RString {
357-
RString::new(
358-
&plus::plus_paths(
359-
pth1.ok().unwrap_or(RString::new("")).to_str(),
360-
pth2.ok().unwrap_or(RString::new("")).to_str()
361-
)[..]
362-
)
340+
RString::new(&plus::plus_paths(to_str(&pth1), to_str(&pth2)))
363341
}
364342

365343
// pub fn pn_prepend_prefix(prefix: MaybeString, relpath: MaybeString){}
366344

367345
pub fn pn_is_relative(pth: MaybeString) -> Boolean {
368-
Boolean::new(
369-
match pth.ok().unwrap_or(RString::new(&MAIN_SEPARATOR.to_string()[..])).to_str().chars().next() {
370-
Some(c) => c != MAIN_SEPARATOR,
371-
None => true
372-
}
373-
)
346+
let path = match &pth {
347+
&Ok(ref ruru_string) => ruru_string.to_str(),
348+
&Err(_) => return Boolean::new(false),
349+
};
350+
Boolean::new(path.as_bytes().get(0) != Some(&SEP))
374351
}
375352

376353
// pub fn pn_root(pth: MaybeString){}

src/relative_path_from.rs

+17-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
use helpers::is_same_path;
1+
use helpers::{is_same_path, to_str};
22
use path_parsing::SEP_STR;
3+
use cleanpath_aggressive::cleanpath_aggressive;
34
extern crate array_tool;
4-
use self::array_tool::vec::{Shift, Join};
55
use self::array_tool::iter::ZipOpt;
6-
use pathname;
76
use ruru;
87
use chop_basename;
98
use pathname::Pathname;
@@ -12,36 +11,38 @@ use ruru::{Exception as Exc, AnyException as Exception};
1211
type MaybeString = Result<ruru::RString, ruru::result::Error>;
1312

1413
pub fn relative_path_from(itself: MaybeString, base_directory: MaybeString) -> Result<Pathname, Exception> {
15-
let dest_directory = pathname::pn_cleanpath_aggressive(itself).to_string();
16-
let base_directory = pathname::pn_cleanpath_aggressive(base_directory).to_string();
14+
let dest_directory = cleanpath_aggressive(to_str(&itself));
15+
let base_directory = cleanpath_aggressive(to_str(&base_directory));
1716

18-
let mut dest_prefix = dest_directory.clone();
19-
let mut dest_names: Vec<String> = vec![];
17+
let mut dest_prefix = dest_directory.as_ref();
18+
let mut dest_names: Vec<&str> = vec![];
2019
loop {
2120
match chop_basename::chop_basename(&dest_prefix.clone()) {
2221
Some((ref dest, ref basename)) => {
23-
dest_prefix = dest.to_string();
22+
dest_prefix = dest;
2423
if basename != &"." {
25-
dest_names.unshift(basename.to_string())
24+
dest_names.push(basename)
2625
}
2726
},
2827
None => break,
2928
}
3029
}
30+
dest_names.reverse();
3131

32-
let mut base_prefix = base_directory.clone();
33-
let mut base_names: Vec<String> = vec![];
32+
let mut base_prefix = base_directory.as_ref();
33+
let mut base_names: Vec<&str> = vec![];
3434
loop {
35-
match chop_basename::chop_basename(&base_prefix.clone()) {
35+
match chop_basename::chop_basename(&base_prefix) {
3636
Some((ref base, ref basename)) => {
37-
base_prefix = base.to_string();
37+
base_prefix = base;
3838
if basename != &"." {
39-
base_names.unshift(basename.to_string())
39+
base_names.push(basename)
4040
}
4141
},
4242
None => break,
4343
}
4444
}
45+
base_names.reverse();
4546

4647
if !is_same_path(&dest_prefix, &base_prefix) {
4748
return Err(
@@ -85,6 +86,7 @@ pub fn relative_path_from(itself: MaybeString, base_directory: MaybeString) -> R
8586
if base_names.is_empty() && dest_names.is_empty() {
8687
Ok(Pathname::new("."))
8788
} else {
88-
Ok(Pathname::new(&base_names.iter().chain(dest_names.iter()).collect::<Vec<&String>>().join(&SEP_STR)))
89+
Ok(Pathname::new(&base_names.iter().chain(dest_names.iter()).map(String::as_str).
90+
collect::<Vec<&str>>().join(&SEP_STR)))
8991
}
9092
}

0 commit comments

Comments
 (0)