Skip to content

Commit

Permalink
[Utils/FileUtils] A file can now be loaded into a byte array
Browse files Browse the repository at this point in the history
- readFileToArray() loads the content of a file into a vector of unsigned characters, which may be useful for some usages

- readFile(), which returned a string has been renamed to readFileToString()
  • Loading branch information
Razakhel committed Dec 20, 2023
1 parent 6f2f066 commit f82df35
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 24 deletions.
8 changes: 7 additions & 1 deletion include/RaZ/Utils/FileUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#define RAZ_FILEUTILS_HPP

#include <string>
#include <vector>

namespace Raz {

Expand All @@ -16,11 +17,16 @@ namespace FileUtils {
/// \return True if the file is readable, false otherwise.
bool isReadable(const FilePath& filePath);

/// Reads a whole file into a byte array.
/// \param filePath Path to the file to read.
/// \return Content of the file.
std::vector<unsigned char> readFileToArray(const FilePath& filePath);

/// Reads a whole file into a string.
/// \note The returned string is not trimmed: there can be spaces or empty lines at the beginning or the end if they exist in the file.
/// \param filePath Path to the file to read.
/// \return Content of the file.
std::string readFile(const FilePath& filePath);
std::string readFileToString(const FilePath& filePath);

} // namespace FileUtils

Expand Down
2 changes: 1 addition & 1 deletion src/RaZ/Render/Shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void Shader::load() const {
return;

Logger::debug("[Shader] Loading (ID: " + std::to_string(m_index) + ", path: '" + m_path + "')...");
loadSource(FileUtils::readFile(m_path));
loadSource(FileUtils::readFileToString(m_path));
Logger::debug("[Shader] Loaded");
}

Expand Down
2 changes: 1 addition & 1 deletion src/RaZ/Script/LuaScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ void LuaScript::loadCode(const std::string& code) {

void LuaScript::loadCodeFromFile(const FilePath& filePath) {
Logger::debug("[LuaScript] Loading code from file ('" + filePath + "')...");
loadCode(FileUtils::readFile(filePath));
loadCode(FileUtils::readFileToString(filePath));
Logger::debug("[LuaScript] Loaded code from file");
}

Expand Down
6 changes: 4 additions & 2 deletions src/RaZ/Script/LuaUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ void LuaWrapper::registerUtilsTypes() {
}

{
sol::table fileUtils = state["FileUtils"].get_or_create<sol::table>();
fileUtils["readFile"] = &FileUtils::readFile;
sol::table fileUtils = state["FileUtils"].get_or_create<sol::table>();
fileUtils["isReadable"] = &FileUtils::isReadable;
fileUtils["readFileToArray"] = &FileUtils::readFileToArray;
fileUtils["readFileToString"] = &FileUtils::readFileToString;
}

{
Expand Down
33 changes: 23 additions & 10 deletions src/RaZ/Utils/FileUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,45 @@

namespace Raz::FileUtils {

bool isReadable(const FilePath& filePath) {
return std::ifstream(filePath).good();
}
namespace {

std::string readFile(const FilePath& filePath) {
std::ifstream file(filePath, std::ios::in | std::ios::binary | std::ios::ate);
template <typename T>
T readFile(const FilePath& filePath) {
std::ifstream file(filePath, std::ios::binary | std::ios::ate);

if (!file)
throw std::runtime_error("Error: Could not open the file '" + filePath + '\'');
throw std::runtime_error("[FileUtils] Could not open the file '" + filePath + '\'');

// Note that tellg() does not necessarily return a size, but rather a mark pointing at a specific place in the file
// When opening a file in binary, however, it is currently pretty much always represented as a byte offset
// When opening a file in binary however, it is currently pretty much always represented as a byte offset
// See: https://stackoverflow.com/a/22986486/3292304
const auto fileSize = file.tellg();

if (fileSize == -1)
throw std::runtime_error("Error: Failed to get the size of the file '" + filePath + '\'');
throw std::runtime_error("[FileUtils] Failed to get the size of the file '" + filePath + '\'');

// Returning at the beginning of the file to read it
file.seekg(0, std::ios::beg);

std::string fileContent;
T fileContent;
fileContent.resize(static_cast<std::size_t>(fileSize));
file.read(fileContent.data(), static_cast<std::streamsize>(fileSize));
file.read(reinterpret_cast<char*>(fileContent.data()), static_cast<std::streamsize>(fileSize));

return fileContent;
}

} // namespace

bool isReadable(const FilePath& filePath) {
return std::ifstream(filePath).good();
}

std::vector<unsigned char> readFileToArray(const FilePath& filePath) {
return readFile<std::vector<unsigned char>>(filePath);
}

std::string readFileToString(const FilePath& filePath) {
return readFile<std::string>(filePath);
}

} // namespace Raz
4 changes: 3 additions & 1 deletion tests/src/RaZ/Script/LuaUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ TEST_CASE("LuaUtils FilePath") {

TEST_CASE("LuaUtils FileUtils") {
CHECK(Raz::LuaWrapper::execute(R"(
assert(FileUtils.readFile(FilePath.new(RAZ_TESTS_ROOT .. "assets/misc/ͳεs†_fílè_测试.τxt")) == "НΣļlõ ωθяŁĐ!\n")
assert(FileUtils.isReadable(FilePath.new(RAZ_TESTS_ROOT .. "assets/misc/ͳεs†_fílè_测试.τxt")))
assert(FileUtils.readFileToArray(FilePath.new(RAZ_TESTS_ROOT .. "assets/misc/ͳεs†_fílè_测试.τxt")):size() == 22)
assert(FileUtils.readFileToString(FilePath.new(RAZ_TESTS_ROOT .. "assets/misc/ͳεs†_fílè_测试.τxt")) == "НΣļlõ ωθяŁĐ!\n")
)"));
}

Expand Down
39 changes: 31 additions & 8 deletions tests/src/RaZ/Utils/FileUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,35 @@ TEST_CASE("FileUtils is readable") {
CHECK_FALSE(Raz::FileUtils::isReadable("this_file_does_not_exist.txt"));
}

TEST_CASE("FileUtils read file") {
std::string fileContent = Raz::FileUtils::readFile(encodedFilePath);
CHECK(fileContent.size() == 22); // This doesn't represent the actual character count due to the encoding
CHECK(fileContent == "НΣļlõ ωθяŁĐ!\n");

fileContent = Raz::FileUtils::readFile(asciiFilePath);
CHECK(fileContent.size() == 13);
CHECK(fileContent == "Hello world!\n");
TEST_CASE("FileUtils read file to array") {
{
const std::vector<unsigned char> fileContent = Raz::FileUtils::readFileToArray(encodedFilePath);
CHECK(fileContent.size() == 22);
CHECK(fileContent == std::vector<unsigned char>({ 0xD0, 0x9D, 0xCE, 0xA3, 0xC4, 0xBC, 'l', 0xC3, 0xB5, ' ',
// \ / \ / \ / | \ /
// Н Σ ļ l õ
0xCF, 0x89, 0xCE, 0xB8, 0xD1, 0x8F, 0xC5, 0x81, 0xC4, 0x90, '!', '\n' }));
// \ / \ / \ / \ / \ / | \/
// ω θ я Ł Đ ! \n
}

{
const std::vector<unsigned char> fileContent = Raz::FileUtils::readFileToArray(asciiFilePath);
CHECK(fileContent.size() == 13);
CHECK(fileContent == std::vector<unsigned char>({ 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n' }));
}
}

TEST_CASE("FileUtils read file to string") {
{
const std::string fileContent = Raz::FileUtils::readFileToString(encodedFilePath);
CHECK(fileContent.size() == 22); // This doesn't represent the actual character count due to the encoding
CHECK(fileContent == "НΣļlõ ωθяŁĐ!\n");
}

{
const std::string fileContent = Raz::FileUtils::readFileToString(asciiFilePath);
CHECK(fileContent.size() == 13);
CHECK(fileContent == "Hello world!\n");
}
}

0 comments on commit f82df35

Please sign in to comment.