Merge pull request #193 from lukash/replace-fstream

Use C-style IO instead of ifstream for parsing
This commit is contained in:
Toru Niina 2022-09-24 00:17:52 +09:00 committed by GitHub
commit c3a3423fb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 149 additions and 24 deletions

View File

@ -142,6 +142,75 @@ BOOST_AUTO_TEST_CASE(test_example_stream)
} }
} }
BOOST_AUTO_TEST_CASE(test_example_file_pointer)
{
FILE * file = fopen("toml/tests/example.toml", "rb");
const auto data = toml::parse(file, "toml/tests/example.toml");
fclose(file);
BOOST_TEST(toml::find<std::string>(data, "title") == "TOML Example");
const auto& owner = toml::find(data, "owner");
{
BOOST_TEST(toml::find<std::string>(owner, "name") == "Tom Preston-Werner");
BOOST_TEST(toml::find<std::string>(owner, "organization") == "GitHub");
BOOST_TEST(toml::find<std::string>(owner, "bio") ==
"GitHub Cofounder & CEO\nLikes tater tots and beer.");
BOOST_TEST(toml::find<toml::offset_datetime>(owner, "dob") ==
toml::offset_datetime(toml::local_date(1979, toml::month_t::May, 27),
toml::local_time(7, 32, 0), toml::time_offset(0, 0)));
}
const auto& database = toml::find(data, "database");
{
BOOST_TEST(toml::find<std::string>(database, "server") == "192.168.1.1");
const std::vector<int> expected_ports{8001, 8001, 8002};
BOOST_CHECK(toml::find<std::vector<int>>(database, "ports") == expected_ports);
BOOST_TEST(toml::find<int >(database, "connection_max") == 5000);
BOOST_TEST(toml::find<bool>(database, "enabled") == true);
}
const auto& servers = toml::find(data, "servers");
{
toml::table alpha = toml::find<toml::table>(servers, "alpha");
BOOST_TEST(toml::get<std::string>(alpha.at("ip")) == "10.0.0.1");
BOOST_TEST(toml::get<std::string>(alpha.at("dc")) == "eqdc10");
toml::table beta = toml::find<toml::table>(servers, "beta");
BOOST_TEST(toml::get<std::string>(beta.at("ip")) == "10.0.0.2");
BOOST_TEST(toml::get<std::string>(beta.at("dc")) == "eqdc10");
BOOST_TEST(toml::get<std::string>(beta.at("country")) == "\xE4\xB8\xAD\xE5\x9B\xBD");
}
const auto& clients = toml::find(data, "clients");
{
toml::array clients_data = toml::find<toml::array>(clients, "data");
std::vector<std::string> expected_name{"gamma", "delta"};
BOOST_CHECK(toml::get<std::vector<std::string>>(clients_data.at(0)) == expected_name);
std::vector<int> expected_number{1, 2};
BOOST_CHECK(toml::get<std::vector<int>>(clients_data.at(1)) == expected_number);
std::vector<std::string> expected_hosts{"alpha", "omega"};
BOOST_CHECK(toml::find<std::vector<std::string>>(clients, "hosts") == expected_hosts);
}
std::vector<toml::table> products =
toml::find<std::vector<toml::table>>(data, "products");
{
BOOST_TEST(toml::get<std::string>(products.at(0).at("name")) ==
"Hammer");
BOOST_TEST(toml::get<std::int64_t>(products.at(0).at("sku")) ==
738594937);
BOOST_TEST(toml::get<std::string>(products.at(1).at("name")) ==
"Nail");
BOOST_TEST(toml::get<std::int64_t>(products.at(1).at("sku")) ==
284758393);
BOOST_TEST(toml::get<std::string>(products.at(1).at("color")) ==
"gray");
}
}
BOOST_AUTO_TEST_CASE(test_fruit) BOOST_AUTO_TEST_CASE(test_fruit)
{ {
const auto data = toml::parse("toml/tests/fruit.toml"); const auto data = toml::parse("toml/tests/fruit.toml");
@ -959,3 +1028,8 @@ BOOST_AUTO_TEST_CASE(test_parse_function_compiles)
BOOST_TEST_MESSAGE("path"); BOOST_TEST_MESSAGE("path");
#endif #endif
} }
BOOST_AUTO_TEST_CASE(test_parse_nonexistent_file)
{
BOOST_CHECK_THROW(toml::parse("nonexistent.toml"), std::ios_base::failure);
}

View File

@ -10,6 +10,18 @@
namespace toml namespace toml
{ {
struct file_io_error : public std::runtime_error
{
public:
file_io_error(int errnum, const std::string& msg, const std::string& fname)
: std::runtime_error(msg + " \"" + fname + "\": " + std::strerror(errnum)),
errno_(errnum)
{}
int get_errno() {return errno_;}
private:
int errno_;
};
struct exception : public std::exception struct exception : public std::exception
{ {
public: public:

View File

@ -5,7 +5,6 @@
#include <istream> #include <istream>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <fstream>
#include "combinator.hpp" #include "combinator.hpp"

View File

@ -2377,27 +2377,14 @@ result<Value, std::string> parse_toml_file(location& loc)
return ok(Value(std::move(data), file, comments)); return ok(Value(std::move(data), file, comments));
} }
} // detail
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY, template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Table = std::unordered_map, template<typename ...> class Table = std::unordered_map,
template<typename ...> class Array = std::vector> template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array> basic_value<Comment, Table, Array>
parse(std::istream& is, std::string fname = "unknown file") parse(std::vector<char>& letters, const std::string& fname)
{ {
using value_type = basic_value<Comment, Table, Array>; using value_type = basic_value<Comment, Table, Array>;
const auto beg = is.tellg();
is.seekg(0, std::ios::end);
const auto end = is.tellg();
const auto fsize = end - beg;
is.seekg(beg);
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<char> letters(static_cast<std::size_t>(fsize));
is.read(letters.data(), fsize);
// append LF. // append LF.
// Although TOML does not require LF at the EOF, to make parsing logic // Although TOML does not require LF at the EOF, to make parsing logic
// simpler, we "normalize" the content by adding LF if it does not exist. // simpler, we "normalize" the content by adding LF if it does not exist.
@ -2435,16 +2422,75 @@ parse(std::istream& is, std::string fname = "unknown file")
return data.unwrap(); return data.unwrap();
} }
} // detail
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Table = std::unordered_map,
template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array>
parse(FILE * file, const std::string& fname)
{
const long beg = std::ftell(file);
if (beg == -1l) {
throw file_io_error(errno, "Failed to access", fname);
}
int res = std::fseek(file, 0, SEEK_END);
if (res != 0) {
throw file_io_error(errno, "Failed to seek", fname);
}
const long end = std::ftell(file);
if (end == -1l) {
throw file_io_error(errno, "Failed to access", fname);
}
const auto fsize = end - beg;
res = std::fseek(file, beg, SEEK_SET);
if (res != 0) {
throw file_io_error(errno, "Failed to seek", fname);
}
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<char> letters(static_cast<std::size_t>(fsize));
std::fread(letters.data(), sizeof(char), static_cast<std::size_t>(fsize), file);
return detail::parse<Comment, Table, Array>(letters, fname);
}
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Table = std::unordered_map,
template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array>
parse(std::istream& is, std::string fname = "unknown file")
{
const auto beg = is.tellg();
is.seekg(0, std::ios::end);
const auto end = is.tellg();
const auto fsize = end - beg;
is.seekg(beg);
// read whole file as a sequence of char
assert(fsize >= 0);
std::vector<char> letters(static_cast<std::size_t>(fsize));
is.read(letters.data(), fsize);
return detail::parse<Comment, Table, Array>(letters, fname);
}
template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY, template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Table = std::unordered_map, template<typename ...> class Table = std::unordered_map,
template<typename ...> class Array = std::vector> template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array> parse(std::string fname) basic_value<Comment, Table, Array> parse(std::string fname)
{ {
std::ifstream ifs(fname.c_str(), std::ios_base::binary); std::ifstream ifs(fname, std::ios_base::binary);
if(!ifs.good()) if(!ifs.good())
{ {
throw std::runtime_error("toml::parse: file open error -> " + fname); throw std::ios_base::failure("toml::parse: Error opening file \"" + fname + "\"");
} }
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
return parse<Comment, Table, Array>(ifs, std::move(fname)); return parse<Comment, Table, Array>(ifs, std::move(fname));
} }
@ -2471,13 +2517,7 @@ template<typename Comment = TOML11_DEFAULT_COMMENT_STRATEGY,
template<typename ...> class Array = std::vector> template<typename ...> class Array = std::vector>
basic_value<Comment, Table, Array> parse(const std::filesystem::path& fpath) basic_value<Comment, Table, Array> parse(const std::filesystem::path& fpath)
{ {
std::ifstream ifs(fpath, std::ios_base::binary); return parse<Comment, Table, Array>(fpath.string());
if(!ifs.good())
{
throw std::runtime_error("toml::parse: file open error -> " +
fpath.string());
}
return parse<Comment, Table, Array>(ifs, fpath.string());
} }
#endif // TOML11_HAS_STD_FILESYSTEM #endif // TOML11_HAS_STD_FILESYSTEM