Skip to content

Commit 8d90afd

Browse files
committed
mex: normalize
1 parent 210f8b1 commit 8d90afd

File tree

10 files changed

+111
-29
lines changed

10 files changed

+111
-29
lines changed

+stdlib/normalize.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
%% NORMALIZE remove redundant elements of path
2+
% optional: mex
3+
%
24
% normalize(p) remove redundant elements of path p
35
% path need not exist, normalized path is returned
46
%
57
%%% Inputs
68
% * p: path to normalize
79
%%% Outputs
810
% * c: normalized path
11+
%
12+
% MEX code is about 4-5x faster than plain Matlab below
913

1014
function n = normalize(p)
1115
arguments

buildfile.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function publishTask(context)
8484
function srcs = get_mex_sources()
8585

8686
pure = "src/pure.cpp";
87-
normal = ["src/normalize.cpp", pure];
87+
normal = ["src/normalize_fs.cpp", pure];
8888

8989
win = [pure, "src/windows.cpp"];
9090

@@ -98,6 +98,7 @@ function publishTask(context)
9898
"src/unlink.cpp", ...
9999
["src/is_admin.cpp", "src/admin_fs.cpp"] ...
100100
"src/is_char_device.cpp", ...
101+
["src/normalize.cpp", normal], ...
101102
["src/parent.cpp", "src/parent_fs.cpp", normal], ...
102103
"src/relative_to.cpp", ...
103104
"src/proximate_to.cpp", ...

example/bench_normalize.m

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
a1 = "c:/a///.//../////b";
22

3-
fno = @() stdlib.normalize(a1, false);
4-
fjava = @() stdlib.normalize(a1, true);
3+
fno = @() stdlib.normalize(a1);
54

65
t_no = timeit(fno);
7-
t_java = timeit(fjava);
86

9-
disp("No Java: " + t_no + " s")
10-
disp("Java: " + t_java + " s")
11-
12-
disp("Java is " + t_no/t_java + " times faster")
7+
disp("No Java: " + t_no + " s")

octave_build.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ function octave_build(overwrite)
1515
fullfile(d, "is_wsl.cpp"), ...
1616
fullfile(d, "is_rosetta.cpp"), ...
1717
fullfile(d, "is_admin.cpp"), ...
18+
fullfile(d, "normalize.cpp"), ...
1819
fullfile(d, "parent.cpp"), ...
1920
fullfile(d, "proximate_to.cpp"), ...
2021
fullfile(d, "relative_to.cpp"), ...

src/ffilesystem.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ bool fs_is_windows();
66
std::size_t fs_get_max_path();
77
std::string fs_as_posix(std::string_view);
88
std::string fs_drop_slash(std::string_view);
9+
std::string fs_normalize(std::string_view path);
910
std::string fs_as_posix(std::string_view);
1011

1112
bool fs_is_url(std::string_view);

src/normalize.cpp

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,43 @@
1+
#include "mex.hpp"
2+
#include "mexAdapter.hpp"
3+
14
#include <string>
2-
#include <string_view>
3-
#include <algorithm> // std::unique
45

5-
#include <filesystem>
6+
#include <vector>
7+
#include <memory>
68

79
#include "ffilesystem.h"
810

911

10-
std::string fs_drop_slash(std::string_view in)
11-
{
12-
// drop all trailing "/" and duplicated internal "/"
13-
14-
std::filesystem::path p(in);
15-
16-
std::string s = p.generic_string();
17-
18-
if (!fs_is_windows() || (s.length() != 3 || p != p.root_path())){
19-
while(s.length() > 1 && s.back() == '/')
20-
s.pop_back();
12+
class MexFunction : public matlab::mex::Function {
13+
public:
14+
void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {
15+
// boilerplate engine & ArrayFactory setup
16+
std::shared_ptr<matlab::engine::MATLABEngine> matlabEng = getEngine();
17+
18+
matlab::data::ArrayFactory factory;
19+
20+
// wrangle Inputs
21+
std::string in;
22+
23+
if (inputs.size() != 1) {
24+
matlabEng->feval(u"error", 0,
25+
std::vector<matlab::data::Array>({ factory.createScalar("Mex: One input required") }));
26+
}
27+
if ((inputs[0].getType() == matlab::data::ArrayType::MATLAB_STRING && inputs[0].getNumberOfElements() == 1)){
28+
matlab::data::TypedArray<matlab::data::MATLABString> stringArr = inputs[0];
29+
in = stringArr[0];
30+
} else if (inputs[0].getType() == matlab::data::ArrayType::CHAR){
31+
matlab::data::CharArray charArr = inputs[0];
32+
in.assign(charArr.begin(), charArr.end());
33+
} else {
34+
matlabEng->feval(u"error", 0,
35+
std::vector<matlab::data::Array>({ factory.createScalar("Mex: First input must be a scalar string or char vector") }));
36+
}
37+
38+
// actual function algorithm / computation
39+
std::string out = fs_normalize(in);
40+
41+
outputs[0] = factory.createScalar(out);
2142
}
22-
23-
s.erase(std::unique(s.begin(), s.end(), [](char a, char b){ return a == '/' && b == '/'; }), s.end());
24-
25-
return s;
26-
}
43+
};

src/normalize_fs.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <string>
2+
#include <string_view>
3+
#include <algorithm> // std::unique
4+
5+
#include <filesystem>
6+
7+
#include "ffilesystem.h"
8+
9+
10+
std::string fs_drop_slash(std::string_view in)
11+
{
12+
// drop all trailing "/" and duplicated internal "/"
13+
14+
std::filesystem::path p(in);
15+
16+
std::string s = p.generic_string();
17+
18+
if (!fs_is_windows() || p != p.root_path()){
19+
while(s.length() > 1 && s.back() == '/')
20+
s.pop_back();
21+
}
22+
23+
s.erase(std::unique(s.begin(), s.end(), [](char a, char b){ return a == '/' && b == '/'; }), s.end());
24+
25+
return s;
26+
}
27+
28+
29+
std::string fs_normalize(std::string_view path)
30+
{
31+
std::filesystem::path p(path);
32+
33+
std::string r = p.lexically_normal().generic_string();
34+
35+
// no trailing slash
36+
if (r.length() > 1 && r.back() == '/' && (!fs_is_windows() || p != p.root_path()))
37+
r.pop_back();
38+
39+
if (r.empty())
40+
r.push_back('.');
41+
42+
return r;
43+
}

src/octave/drop_slash.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <string>
44

55
#include "pure.cpp"
6-
#include "normalize.cpp"
6+
#include "normalize_fs.cpp"
77

88

99
DEFUN_DLD (drop_slash, args, nargout,

src/octave/normalize.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <octave/oct.h>
2+
3+
#include <string>
4+
5+
#include "pure.cpp"
6+
#include "normalize_fs.cpp"
7+
8+
9+
DEFUN_DLD (normalize, args, nargout,
10+
"normalize path")
11+
{
12+
if (args.length() != 1){
13+
octave_stdout << "Oct: One input required\n";
14+
return octave_value("");
15+
}
16+
17+
std::string out = fs_normalize(args(0).string_value());
18+
19+
return octave_value(out);
20+
}

src/octave/parent.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include <filesystem>
55

66
#include "pure.cpp"
7-
#include "normalize.cpp"
7+
#include "normalize_fs.cpp"
88

99

1010
DEFUN_DLD (parent, args, nargout,

0 commit comments

Comments
 (0)