Commit fea84635 authored by binji's avatar binji Committed by Commit bot

[NaCl SDK] Rewrite nacl_io::Path to avoid STL.

Path used to be a std::vector<std::string>. Now it is just a C array of size
PATH_MAX.

The code has a lot of nasty edge cases because it is trying to match the
previous behavior. Really, all this code should be axed -- it is incorrect to
handle paths the way nacl_io does currently. AFAICT, the only place that should
have to handle paths is the path lookup routine.

Fixing that is a much larger change.

BUG=none
R=sbc@chromium.org

Review URL: https://codereview.chromium.org/1016873004

Cr-Commit-Position: refs/heads/master@{#321386}
parent 821dbcff
......@@ -4,6 +4,7 @@
#include "nacl_io/path.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <string>
......@@ -12,8 +13,13 @@
namespace nacl_io {
Path::Path() : len_(0) {
path_[0] = 0;
}
Path::Path(const Path& path) {
paths_ = path.paths_;
len_ = path.len_;
strcpy(path_, path.path_);
}
Path::Path(const std::string& path) {
......@@ -21,24 +27,54 @@ Path::Path(const std::string& path) {
}
bool Path::IsAbsolute() const {
return !paths_.empty() && paths_[0] == "/";
return path_[0] == '/';
}
const std::string& Path::Part(size_t index) const {
return paths_[index];
std::string Path::Part(size_t index) const {
if (IsAbsolute() && index == 0) {
return std::string("/");
}
const char* start = &path_[0];
size_t slashes = 0;
const char* p;
for (p = &path_[0]; *p; p++) {
if (*p == '/') {
if (++slashes == index + 1)
break;
start = p + 1;
}
}
return std::string(start, p - start);
}
size_t Path::Size() const {
return paths_.size();
if (len_ == 0)
return 0;
const char* p = &path_[0];
if (len_ == 1 && *p == '/') {
return 1;
}
size_t count = 1;
for (; *p; p++) {
if (*p == '/')
count++;
}
return count;
}
bool Path::IsRoot() const {
return paths_.empty() || (paths_.size() == 1 && paths_[0] == "/");
return strcmp(path_, "/") == 0;
}
Path& Path::MakeRelative() {
if (IsAbsolute()) {
paths_.erase(paths_.begin());
memmove(&path_[0], &path_[1], PATH_MAX - 1);
len_--;
}
return *this;
}
......@@ -47,14 +83,19 @@ Path& Path::Append(const Path& path) {
// Appending an absolute path effectivly sets the path, ignoring
// the current contents.
if (path.IsAbsolute()) {
paths_ = path.paths_;
strcpy(path_, path.path_);
} else {
for (size_t index = 0; index < path.paths_.size(); index++) {
paths_.push_back(path.paths_[index]);
strncat(path_, "/", PATH_MAX - len_ - 1);
len_++;
strncat(path_, path.path_, PATH_MAX - len_ - 1);
len_ += path.len_;
if (len_ >= PATH_MAX - 1) {
len_ = PATH_MAX - 1;
}
}
paths_ = Normalize(paths_);
Normalize();
return *this;
}
......@@ -62,142 +103,157 @@ Path& Path::Append(const std::string& path) {
return Append(Path(path));
}
Path& Path::Set(const StringArray_t path) {
paths_ = Normalize(path);
return *this;
}
Path& Path::Set(const std::string& path) {
return Set(Split(path));
strncpy(path_, path.c_str(), PATH_MAX - 1);
path_[PATH_MAX - 1] = 0;
len_ = path.length();
if (len_ > PATH_MAX - 1)
len_ = PATH_MAX - 1;
Normalize();
return *this;
}
Path Path::Parent() const {
Path out;
out.paths_ = paths_;
if (out.paths_.size())
out.paths_.pop_back();
return out;
const char* last_slash = strrchr(path_, '/');
if (last_slash) {
Path out;
if (last_slash == &path_[0]) {
out.len_ = 1;
strcpy(out.path_, "/");
} else {
out.len_ = last_slash - &path_[0];
strncpy(out.path_, path_, out.len_);
out.path_[out.len_] = 0;
}
return out;
}
return Path(*this);
}
std::string Path::Basename() const {
if (paths_.size())
return paths_.back();
return std::string();
}
if (IsRoot())
return std::string(path_);
std::string Path::Join() const {
return Range(paths_, 0, paths_.size());
}
const char* last_slash = strrchr(path_, '/');
if (last_slash)
return std::string(last_slash + 1, path_ + len_ - (last_slash + 1));
std::string Path::Range(size_t start, size_t end) const {
return Range(paths_, start, end);
return std::string(path_);
}
StringArray_t Path::Split() const {
return paths_;
std::string Path::Join() const {
return std::string(path_);
}
// static
StringArray_t Path::Normalize(const StringArray_t& paths) {
StringArray_t path_out;
for (size_t index = 0; index < paths.size(); index++) {
const std::string& curr = paths[index];
// Check if '/' was used excessively in the path.
// For example, in cd Desktop/////
if (curr == "/" && index != 0)
continue;
std::string Path::Range(size_t start, size_t end) const {
assert(start <= end);
// Check for '.' in the path and remove it
if (curr == ".")
continue;
const char* pstart = &path_[0];
const char* pend = &path_[len_];
// Check for '..'
if (curr == "..") {
// If the path is empty, or "..", then add ".."
if (path_out.empty() || path_out.back() == "..") {
path_out.push_back(curr);
continue;
}
if (IsAbsolute() && start == 0 && end == 1)
return std::string("/");
// If the path is at root, "/.." = "/"
if (path_out.back() == "/") {
continue;
}
size_t slashes = 0;
for (const char* p = &path_[0]; *p; p++) {
if (*p == '/') {
++slashes;
if (slashes == start)
pstart = p + 1;
// if we are already at root, then stay there (root/.. -> root)
if (path_out.back() == "/") {
continue;
if (slashes == end) {
pend = p;
break;
}
// otherwise, pop off the top path component
path_out.pop_back();
continue;
}
// By now, we should have handled end cases so just append.
path_out.push_back(curr);
}
// If the path was valid, but now it's empty, return self
if (path_out.empty())
path_out.push_back(".");
return path_out;
}
if (pstart > pend)
return std::string();
// static
std::string Path::Join(const StringArray_t& paths) {
return Range(paths, 0, paths.size());
return std::string(pstart, pend - pstart);
}
// static
std::string Path::Range(const StringArray_t& paths, size_t start, size_t end) {
std::string out_path;
size_t index = start;
if (end > paths.size())
end = paths.size();
void Path::Normalize() {
char* outp = &path_[0];
const char* start = outp;
const char* part_start = start;
const char* next_slash;
bool is_absolute = false;
// If this is an absolute path, paths[0] == "/". In this case, we don't want
// to add an additional / separator.
if (start == 0 && end > 0 && paths[0] == "/") {
out_path += "/";
index++;
if (IsAbsolute()) {
// Absolute path. Append the slash, then continue the algorithm as if the
// path were relative.
start++;
outp++;
part_start++;
is_absolute = true;
}
for (; index < end; index++) {
out_path += paths[index];
if (index < end - 1)
out_path += "/";
}
do {
next_slash = strchr(part_start, '/');
const char* part_end = next_slash;
if (!part_end)
part_end = part_start + strlen(part_start);
size_t part_len = part_end - part_start;
bool should_append = true;
if (part_len == 0) {
// Don't append if the part is empty.
should_append = false;
} else if (part_len == 1 && part_start[0] == '.') {
// Don't append "."
should_append = false;
} else if (part_len == 2 && part_start[0] == '.' && part_start[1] == '.') {
// If part is "..", only append if the output is empty or already has
// ".." at the end.
if (outp == start ||
(outp - start >= 2 && outp[-1] == '.' && outp[-2] == '.')) {
should_append = !is_absolute;
} else {
should_append = false;
// Move outp backward to the one past the previous slash, or to the
// beginning of the string. Unless outp == start, outp[-1] is a '/'.
if (outp > start)
--outp;
while (outp > start && outp[0] != '/')
--outp;
}
}
return out_path;
}
if (should_append) {
// Append [part_start, part_end) to outp.
if (outp != start) {
// Append slash to separate from previous path.
*outp++ = '/';
}
// static
StringArray_t Path::Split(const std::string& path) {
StringArray_t path_split;
StringArray_t components;
// Only need to copy bytes when the pointers are different.
if (outp != part_start) {
memmove(outp, part_start, part_len);
}
sdk_util::SplitString(path, '/', &path_split);
outp += part_len;
}
if (path[0] == '/')
components.push_back("/");
part_start = next_slash + 1;
} while (next_slash);
// Copy path_split to components, removing empty path segments.
for (StringArray_t::const_iterator it = path_split.begin();
it != path_split.end();
++it) {
if (!it->empty())
components.push_back(*it);
// Return '.' instead of an empty path.
if (outp == start && !is_absolute) {
*outp++ = '.';
}
return components;
*outp = 0;
len_ = outp - &path_[0];
}
Path& Path::operator=(const Path& p) {
paths_ = p.paths_;
len_ = p.len_;
strcpy(path_, p.path_);
return *this;
}
......@@ -205,4 +261,12 @@ Path& Path::operator=(const std::string& p) {
return Set(p);
}
bool Path::operator==(const Path& other) {
return len_ == other.len_ && strncmp(path_, other.path_, len_) == 0;
}
bool Path::operator!=(const Path& other) {
return !operator==(other);
}
} // namespace nacl_io
......@@ -5,6 +5,8 @@
#ifndef LIBRARIES_NACL_IO_PATH_H_
#define LIBRARIES_NACL_IO_PATH_H_
#include <limits.h>
#include <string>
#include <vector>
......@@ -12,16 +14,10 @@
namespace nacl_io {
typedef std::vector<std::string> StringArray_t;
class Path {
public:
Path() {}
Path();
Path(const Path& path);
// This constructor splits path by '/' as a starting point for this Path.
// If the path begins with the character '/', the path is considered
// to be absolute.
explicit Path(const std::string& path);
// Return true of the first path item is '/'.
......@@ -31,7 +27,7 @@ class Path {
bool IsRoot() const;
// Return a part of the path
const std::string& Part(size_t index) const;
std::string Part(size_t index) const;
// Return the number of path parts
size_t Size() const;
......@@ -41,7 +37,6 @@ class Path {
Path& Append(const std::string& path);
Path& Set(const std::string& path);
Path& MakeRelative();
Path& Set(const StringArray_t path);
// Return the parent path.
Path Parent() const;
......@@ -49,26 +44,19 @@ class Path {
std::string Join() const;
std::string Range(size_t start, size_t end) const;
StringArray_t Split() const;
// Collapse the string list removing extraneous '.', '..' path components
static StringArray_t Normalize(const StringArray_t& paths);
static std::string Join(const StringArray_t& paths);
static std::string Range(const StringArray_t& paths,
size_t start,
size_t end);
static StringArray_t Split(const std::string& paths);
// Operator versions
Path& operator=(const Path& p);
Path& operator=(const std::string& str);
bool operator==(const Path& other) { return Split() == other.Split(); }
bool operator!=(const Path& other) { return !operator==(other); }
bool operator==(const Path& other);
bool operator!=(const Path& other);
private:
// Internal representation of the path stored an array of string representing
// the directory traversal. The first string is a "/" if this is an absolute
// path.
StringArray_t paths_;
// Collapse the string list removing extraneous '.', '..' path components
void Normalize();
size_t len_;
char path_[PATH_MAX];
};
} // namespace nacl_io
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment