1/*******************************************************************************
2 Copyright (c) The Taichi Authors (2016- ). All Rights Reserved.
3 The use of this software is governed by the LICENSE file.
4*******************************************************************************/
5
6#pragma once
7
8#include "taichi/common/core.h"
9#include <string>
10#include <vector>
11#include <cstdio>
12#include <cstdlib>
13#include <sys/stat.h>
14
15#if defined(TI_PLATFORM_WINDOWS)
16#include <filesystem>
17#else // POSIX
18#include <unistd.h>
19#include <dirent.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#endif
23
24namespace taichi {
25
26inline bool path_exists(const std::string &dir) {
27 struct stat buffer;
28 return stat(dir.c_str(), &buffer) == 0;
29}
30
31// TODO: move to std::filesystem after it's nonexperimental on all platforms
32inline void create_directories(const std::string &dir) {
33#if defined(TI_PLATFORM_WINDOWS)
34 std::filesystem::create_directories(dir);
35#else
36 int return_code = std::system(fmt::format("mkdir -p {}", dir).c_str());
37 if (return_code != 0) {
38 throw std::runtime_error(
39 fmt::format("Unable to create directory at: {dir}").c_str());
40 }
41#endif
42}
43
44template <typename First, typename... Path>
45inline std::string join_path(First &&path, Path &&...others) {
46 if constexpr (sizeof...(others) == 0) {
47 return std::string(path);
48 } else {
49 return std::string(path) + "/" +
50 taichi::join_path(std::forward<Path>(others)...);
51 }
52 return "";
53}
54
55inline bool remove(const std::string &path) {
56 return std::remove(path.c_str()) == 0;
57}
58
59template <typename Visitor> // void(const std::string &name, bool is_dir)
60inline bool traverse_directory(const std::string &dir, Visitor v) {
61#if defined(TI_PLATFORM_WINDOWS)
62 namespace fs = std::filesystem;
63 std::error_code ec{};
64 auto iter = fs::directory_iterator(dir, ec);
65 if (ec) {
66 return false;
67 }
68 for (auto &f : iter) {
69 v(f.path().filename().string(), f.is_directory());
70 }
71 return true;
72#else // POSIX
73 struct dirent *f = nullptr;
74 DIR *directory = ::opendir(dir.c_str());
75 if (!directory) {
76 return false;
77 }
78 while ((f = ::readdir(directory))) {
79 auto fullpath = join_path(dir, f->d_name);
80 struct stat stat_buf;
81 auto ret = ::stat(fullpath.c_str(), &stat_buf);
82 TI_ASSERT(ret == 0);
83 v(f->d_name, S_ISDIR(stat_buf.st_mode));
84 }
85 auto ret = ::closedir(directory);
86 TI_ASSERT(ret == 0);
87 return true;
88#endif
89}
90
91inline std::string filename_extension(const std::string &filename) {
92 std::string postfix;
93 auto pos = filename.find_last_of('.');
94 if (pos != std::string::npos) {
95 postfix = filename.substr(pos + 1);
96 }
97 return postfix;
98}
99
100template <typename T>
101void write_to_disk(const T &dat, std::string fn) {
102 FILE *f = fopen(fn.c_str(), "wb");
103 fwrite(&dat, sizeof(dat), 1, f);
104 fclose(f);
105}
106
107template <typename T>
108bool read_from_disk(T &dat, std::string fn) {
109 FILE *f = fopen(fn.c_str(), "rb");
110 if (f == nullptr) {
111 return false;
112 }
113 size_t ret = fread(&dat, sizeof(dat), 1, f);
114 if (ret != sizeof(dat)) {
115 return false;
116 }
117 fclose(f);
118 return true;
119}
120
121template <typename T>
122void write_vector_to_disk(std::vector<T> *p_vec, std::string fn) {
123 std::vector<T> &vec = *p_vec;
124 FILE *f = fopen(fn.c_str(), "wb");
125 size_t length = vec.size();
126 fwrite(&length, sizeof(length), 1, f);
127 fwrite(&vec[0], sizeof(vec[0]), length, f);
128 fclose(f);
129}
130
131template <typename T>
132bool read_vector_from_disk(std::vector<T> *p_vec, std::string fn) {
133 std::vector<T> &vec = *p_vec;
134 FILE *f = fopen(fn.c_str(), "rb");
135 if (f == nullptr) {
136 return false;
137 }
138 size_t length;
139 size_t ret = fread(&length, sizeof(length), 1, f);
140 if (ret != 1) {
141 return false;
142 }
143 vec.resize(length);
144 ret = fread(&vec[0], sizeof(vec[0]), length, f);
145 if (ret != length) {
146 return false;
147 }
148 fclose(f);
149 return true;
150}
151
152} // namespace taichi
153