From 0442a74b380ed8ac44aacae13b2a028712ce89e9 Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Mon, 27 Nov 2023 01:31:45 +0100 Subject: [PATCH] Implement and test util::file_seek --- include/osmium/util/file.hpp | 16 ++++++++++ test/CMakeLists.txt | 1 + test/t/io/test_file_seek.cpp | 62 ++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 test/t/io/test_file_seek.cpp diff --git a/include/osmium/util/file.hpp b/include/osmium/util/file.hpp index 23c53db8..2a2e22f2 100644 --- a/include/osmium/util/file.hpp +++ b/include/osmium/util/file.hpp @@ -222,6 +222,22 @@ namespace osmium { return static_cast(offset); } + /** + * Set current offset into file. + * + * @param fd Open file descriptor. + * @param offset Desired absolute offset into the file + */ + inline void file_seek(int fd, size_t offset) noexcept { +#ifdef _MSC_VER + osmium::detail::disable_invalid_parameter_handler diph; + // https://msdn.microsoft.com/en-us/library/1yee101t.aspx + _lseeki64(fd, offset, SEEK_SET); +#else + ::lseek(fd, offset, SEEK_SET); +#endif + } + /** * Check whether the file descriptor refers to a TTY. * diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f5c696d2..6eb6b811 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -187,6 +187,7 @@ add_unit_test(io test_compression_factory) add_unit_test(io test_file_formats) add_unit_test(io test_nocompression) add_unit_test(io test_output_utils) +add_unit_test(io test_file_seek) add_unit_test(io test_string_table) add_unit_test(io test_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES}) diff --git a/test/t/io/test_file_seek.cpp b/test/t/io/test_file_seek.cpp new file mode 100644 index 00000000..3d69e819 --- /dev/null +++ b/test/t/io/test_file_seek.cpp @@ -0,0 +1,62 @@ +#include "catch.hpp" + +#include "utils.hpp" + +#include +#include + +/** + * Can read and seek around in files. + */ +TEST_CASE("Seek and read in files") { + /* gzipped data contains very few repetitions in the binary file format, + * which makes it easy to identify any problems. */ + int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.gz")); + struct seek_expectation { + size_t offset; + unsigned char eight_bytes[8]; + }; + const seek_expectation expectations[] = { + { 0x00, {0x1f, 0x8b, 0x08, 0x08, 0x19, 0x4a, 0x18, 0x54} }, + { 0x00, {0x1f, 0x8b, 0x08, 0x08, 0x19, 0x4a, 0x18, 0x54} }, /* repeat / jump back */ + { 0x21, {0x56, 0xc6, 0x18, 0xc3, 0xea, 0x6d, 0x4f, 0xe0} }, /* unaligned */ + { 0xb3, {0xcd, 0x0a, 0xe7, 0x8f, 0xde, 0x00, 0x00, 0x00} }, /* close to end */ + { 0x21, {0x56, 0xc6, 0x18, 0xc3, 0xea, 0x6d, 0x4f, 0xe0} }, /* "long" backward jump */ + }; + for (const auto& expect : expectations) { + printf("Trying to seek to %lu\n", expect.offset); + char actual_eight_bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + osmium::util::file_seek(fd, expect.offset); + bool did_actually_read = osmium::io::detail::read_exactly(fd, &actual_eight_bytes[0], 8); + REQUIRE(did_actually_read); + for (int i = 0; i < 8; ++i) { + REQUIRE(expect.eight_bytes[i] == static_cast(actual_eight_bytes[i])); + } + } +} + +TEST_CASE("Seek close to end of file") { + /* gzipped data contains very few repetitions in the binary file format, + * which makes it easy to identify any problems. */ + int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.gz")); + REQUIRE(osmium::util::file_size(with_data_dir("t/io/data.osm.gz")) == 187); + char actual_eight_bytes[8] = {1, 1, 1, 1, 1, 1, 1, 1}; + osmium::util::file_seek(fd, 186); + auto actually_read = osmium::io::detail::reliable_read(fd, &actual_eight_bytes[0], 8); + REQUIRE(actually_read == 1); + REQUIRE(actual_eight_bytes[0] == 0); + REQUIRE(actual_eight_bytes[1] == 1); +} + +TEST_CASE("Seek to exact end of file") { + /* gzipped data contains very few repetitions in the binary file format, + * which makes it easy to identify any problems. */ + int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.gz")); + REQUIRE(osmium::util::file_size(with_data_dir("t/io/data.osm.gz")) == 187); + char actual_eight_bytes[8] = {1, 1, 1, 1, 1, 1, 1, 1}; + osmium::util::file_seek(fd, 187); + auto actually_read = osmium::io::detail::reliable_read(fd, &actual_eight_bytes[0], 8); + REQUIRE(actually_read == 0); + REQUIRE(actual_eight_bytes[0] == 1); + REQUIRE(actual_eight_bytes[1] == 1); +}