You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by dr...@apache.org on 2018/09/20 16:13:53 UTC

[trafficserver] branch master updated: ts_file: Simple sketch of std::filesystem for internal use.

This is an automated email from the ASF dual-hosted git repository.

dragon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 8c6be99  ts_file: Simple sketch of std::filesystem for internal use.
8c6be99 is described below

commit 8c6be99dbe637101561a806a8ea5756ec5804ea7
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Wed Sep 12 09:49:37 2018 -0500

    ts_file: Simple sketch of std::filesystem for internal use.
---
 include/tscore/ts_file.h              | 247 ++++++++++++++++++++++++++++++++++
 src/tscore/Makefile.am                |   4 +-
 src/tscore/ts_file.cc                 | 126 +++++++++++++++++
 src/tscore/unit_tests/test_ts_file.cc |  69 ++++++++++
 4 files changed, 445 insertions(+), 1 deletion(-)

diff --git a/include/tscore/ts_file.h b/include/tscore/ts_file.h
new file mode 100644
index 0000000..0d2ecf9
--- /dev/null
+++ b/include/tscore/ts_file.h
@@ -0,0 +1,247 @@
+/** @file
+
+  Simple path and file utilities.
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <string_view>
+#include <array>
+#include <system_error>
+#include <sys/stat.h>
+#include "tscore/ink_memory.h"
+#include "tscpp/util/TextView.h"
+#include "tscore/BufferWriter.h"
+
+namespace ts
+{
+namespace file
+{
+  /** Utility class for file system paths.
+   */
+  class path
+  {
+    using self_type = path;
+
+  public:
+    using value_type = char;
+    using string_type = std::string;
+    static constexpr char preferred_separator = value_type{'/'};
+
+    /// Default construct empty path.
+    path() = default;
+
+    /// Copy constructor - copies the path.
+    path(const self_type &that) = default;
+
+    /// Move constructor.
+    path(self_type &&that) = default;
+
+    /// Construct from a null terminated string.
+    explicit path(const char *src);
+
+    /// Construct from a string view.
+    path(std::string_view src);
+    //  template < typename ... Args > explicit path(std::string_view base, Args... rest);
+
+    /// Move from an existing string
+    path(std::string &&that);
+
+    /// Replace the path with a copy of @a that.
+    self_type &operator=(const self_type &that) = default;
+
+    /// Replace the path with the contents of @a that.
+    self_type &operator=(self_type &&that) = default;
+
+    /// Assign @a p as the path.
+    self_type &operator=(std::string_view p);
+
+    /** Append or replace path with @a that.
+     *
+     * If @a that is absolute, it replaces @a this. Otherwise @a that is appended with exactly one
+     * separator.
+     *
+     * @param that Filesystem path.
+     * @return @a this
+     */
+    self_type &operator/=(const self_type &that);
+    self_type &operator/=(std::string_view that);
+
+    /// Check if the path is empty.
+    bool empty() const;
+
+    /// Check if the path is absolute.
+    bool is_absolute() const;
+
+    /// Check if the path is not absolute.
+    bool is_relative() const;
+
+    /// Access the path explicitly.
+    char const *c_str() const;
+
+    /// Get a copy of the path.
+    std::string string() const;
+
+  protected:
+    std::string _path; ///< File path.
+  };
+
+  /// Information about a file.
+  class file_status
+  {
+    using self_type = file_status;
+
+  public:
+  protected:
+    struct ::stat _stat; ///< File information.
+
+    friend self_type status(const path &, std::error_code &) noexcept;
+
+    friend int file_type(const self_type &);
+    friend uintmax_t file_size(const self_type &);
+    friend bool is_regular_file(const file_status &);
+    friend bool is_dir(const file_status &);
+    friend bool is_char_device(const file_status &);
+    friend bool is_block_device(const file_status &);
+  };
+
+  /** Get the status of the file at @a p.
+   *
+   * @param p Path to file.
+   * @param ec Error code return.
+   * @return Status of the file.
+   */
+  file_status status(const path &p, std::error_code &ec) noexcept;
+
+  // Related free functions.
+  // These are separate because they are not part of std::filesystem::path.
+
+  /// Return the file type value.
+  int file_type(const file_status &fs);
+
+  /// Check if the path is to a regular file.
+  bool is_regular_file(const file_status &fs);
+
+  /// Check if the path is to a directory.
+  bool is_dir(const file_status &p);
+
+  /// Check if the path is to a character device.
+  bool is_char_device(const file_status &fs);
+
+  /// Check if the path is to a block device.
+  bool is_block_device(const file_status &fs);
+
+  /// Size of the file or block device.
+  uintmax_t file_size(const file_status &fs);
+
+  /// Check if file is readable.
+  bool is_readable(const path &s);
+
+  /** Load the file at @a p into a @c std::string.
+   *
+   * @param p Path to file
+   * @return The contents of the file.
+   */
+  std::string load(const path &p, std::error_code &ec);
+  /* ------------------------------------------------------------------- */
+
+  inline path::path(char const *src) : _path(src) {}
+
+  inline path::path(std::string_view base) : _path(base) {}
+
+  inline path::path(std::string &&that) : _path(std::move(that)) {}
+
+  inline path &
+  path::operator=(std::string_view p)
+  {
+    _path.assign(p);
+    return *this;
+  }
+
+  inline char const *
+  path::c_str() const
+  {
+    return _path.c_str();
+  }
+
+  inline std::string
+  path::string() const
+  {
+    return _path;
+  }
+
+  inline bool
+  path::empty() const
+  {
+    return _path.empty();
+  }
+
+  inline bool
+  path::is_absolute() const
+  {
+    return !_path.empty() && preferred_separator == _path[0];
+  }
+
+  inline bool
+  path::is_relative() const
+  {
+    return !this->is_absolute();
+  }
+
+  inline path &
+  path::operator/=(const self_type &that)
+  {
+    return *this /= std::string_view(that._path);
+  }
+
+  /** Combine two strings as file paths.
+
+       @return A @c path with the combined path.
+  */
+  inline path
+  operator/(const path &lhs, const path &rhs)
+  {
+    return path(lhs) /= rhs;
+  }
+
+  inline path
+  operator/(path &&lhs, const path &rhs)
+  {
+    return path(std::move(lhs)) /= rhs;
+  }
+
+  inline path
+  operator/(const path &lhs, std::string_view rhs)
+  {
+    return path(lhs) /= rhs;
+  }
+
+  inline path
+  operator/(path &&lhs, std::string_view rhs)
+  {
+    return path(std::move(lhs)) /= rhs;
+  }
+
+  /* ------------------------------------------------------------------- */
+} // namespace file
+} // namespace ts
+/* ------------------------------------------------------------------- */
diff --git a/src/tscore/Makefile.am b/src/tscore/Makefile.am
index 8e82d16..297b341 100644
--- a/src/tscore/Makefile.am
+++ b/src/tscore/Makefile.am
@@ -208,6 +208,7 @@ libtscore_la_SOURCES = \
 	Tokenizer.h \
 	Trie.h \
 	TsBuffer.h \
+	ts_file. h ts_file.cc \
 	Version.cc \
 	X509HostnameValidator.cc \
 	X509HostnameValidator.h
@@ -274,7 +275,8 @@ test_tscore_SOURCES = \
 	unit_tests/test_Ptr.cc \
 	unit_tests/test_Regex.cc \
 	unit_tests/test_Scalar.cc \
-	unit_tests/test_scoped_resource.cc
+	unit_tests/test_scoped_resource.cc \
+	unit_tests/test_ts_file.cc
 
 CompileParseRules_SOURCES = CompileParseRules.cc
 
diff --git a/src/tscore/ts_file.cc b/src/tscore/ts_file.cc
new file mode 100644
index 0000000..3467176
--- /dev/null
+++ b/src/tscore/ts_file.cc
@@ -0,0 +1,126 @@
+/** @file
+
+    Minimalist version of std::filesystem.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+    agreements.  See the NOTICE file distributed with this work for additional information regarding
+    copyright ownership.  The ASF licenses this file to you under the Apache License, Version 2.0
+    (the "License"); you may not use this file except in compliance with the License.  You may
+    obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software distributed under the
+    License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+    express or implied. See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+#include "tscore/ts_file.h"
+#include <fcntl.h>
+
+namespace ts
+{
+namespace file
+{
+  path &
+  path::operator/=(std::string_view that)
+  {
+    if (!that.empty()) { // don't waste time appending nothing.
+      if (that.front() == preferred_separator || _path.empty()) {
+        _path.assign(that);
+      } else {
+        if (_path.back() == preferred_separator) {
+          _path.reserve(_path.size() + that.size());
+        } else {
+          _path.reserve(_path.size() + that.size() + 1);
+          _path.push_back(preferred_separator);
+        }
+        _path.append(that);
+      }
+    }
+    return *this;
+  }
+
+  file_status
+  status(const path &p, std::error_code &ec) noexcept
+  {
+    file_status zret;
+    if (::stat(p.c_str(), &zret._stat) >= 0) {
+      ec.clear();
+    } else {
+      ec = std::error_code(errno, std::system_category());
+    }
+    return zret;
+  }
+
+  int
+  file_type(const file_status &fs)
+  {
+    return fs._stat.st_mode & S_IFMT;
+  }
+
+  uintmax_t
+  file_size(const file_status &fs)
+  {
+    return fs._stat.st_size;
+  }
+
+  bool
+  is_char_device(const file_status &fs)
+  {
+    return file_type(fs) == S_IFCHR;
+  }
+
+  bool
+  is_block_device(const file_status &fs)
+  {
+    return file_type(fs) == S_IFBLK;
+  }
+
+  bool
+  is_regular_file(const file_status &fs)
+  {
+    return file_type(fs) == S_IFREG;
+  }
+
+  bool
+  is_dir(const file_status &fs)
+  {
+    return file_type(fs) == S_IFDIR;
+  }
+
+  bool
+  is_readable(const path &p)
+  {
+    return 0 == access(p.c_str(), R_OK);
+  }
+
+  std::string
+  load(const path &p, std::error_code &ec)
+  {
+    std::string zret;
+    ats_scoped_fd fd(::open(p.c_str(), O_RDONLY));
+    ec.clear();
+    if (fd < 0) {
+      ec = std::error_code(errno, std::system_category());
+    } else {
+      struct stat info;
+      if (0 != ::fstat(fd, &info)) {
+        ec = std::error_code(errno, std::system_category());
+      } else {
+        int n = info.st_size;
+        zret.resize(n);
+        auto read_len = ::read(fd, const_cast<char *>(zret.data()), n);
+        if (read_len < n) {
+          ec = std::error_code(errno, std::system_category());
+        }
+      }
+    }
+    return zret;
+  }
+
+} // namespace file
+} // namespace ts
diff --git a/src/tscore/unit_tests/test_ts_file.cc b/src/tscore/unit_tests/test_ts_file.cc
new file mode 100644
index 0000000..e7a49db
--- /dev/null
+++ b/src/tscore/unit_tests/test_ts_file.cc
@@ -0,0 +1,69 @@
+/** @file
+
+    ts::file unit tests.
+
+    @section license License
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#include <iostream>
+
+#include "tscore/ts_file.h"
+#include "../../../tests/include/catch.hpp"
+
+using ts::file::path;
+
+// --------------------
+TEST_CASE("ts_file", "[libts][ts_file]")
+{
+  path p1("/home");
+  REQUIRE(p1.string() == "/home");
+  auto p2 = p1 / "bob";
+  REQUIRE(p2.string() == "/home/bob");
+  p2 = p2 / "git/ats/";
+  REQUIRE(p2.string() == "/home/bob/git/ats/");
+  p2 /= "lib/ts";
+  REQUIRE(p2.string() == "/home/bob/git/ats/lib/ts");
+  p2 /= "/home/dave";
+  REQUIRE(p2.string() == "/home/dave");
+  path p3 = path("/home/dave") / "git/tools";
+  REQUIRE(p3.string() == "/home/dave/git/tools");
+}
+
+TEST_CASE("ts_file_io", "[libts][ts_file_io]")
+{
+  path file("unit_tests/test_ts_file.cc");
+  std::error_code ec;
+  std::string content = ts::file::load(file, ec);
+  REQUIRE(ec.value() == 0);
+  REQUIRE(content.size() > 0);
+  REQUIRE(content.find("ts::file::path") != content.npos);
+
+  // Check some file properties.
+  REQUIRE(ts::file::is_readable(file) == true);
+  auto fs = ts::file::status(file, ec);
+  REQUIRE(ec.value() == 0);
+  REQUIRE(ts::file::is_dir(fs) == false);
+  REQUIRE(ts::file::is_regular_file(fs) == true);
+
+  // Failure case.
+  file    = "unit-tests/no_such_file.txt";
+  content = ts::file::load(file, ec);
+  REQUIRE(ec.value() == 2);
+  REQUIRE(ts::file::is_readable(file) == false);
+}