diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 89a84071..f99f1771 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,6 +1,8 @@ add_library(libprjxray - memory_mapped_file.cc) + memory_mapped_file.cc + segbits_file_reader.cc) target_include_directories(libprjxray PUBLIC "include") +target_link_libraries(libprjxray absl::strings) if (PRJXRAY_BUILD_TESTING) add_executable(memory_mapped_file_test memory_mapped_file_test.cc) @@ -8,4 +10,10 @@ if (PRJXRAY_BUILD_TESTING) add_test(NAME memory_mapped_file_test COMMAND memory_mapped_file_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data) + + add_executable(segbits_file_reader_test segbits_file_reader_test.cc) + target_link_libraries(segbits_file_reader_test libprjxray gtest_main) + add_test(NAME segbits_file_reader_test + COMMAND segbits_file_reader_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test_data) endif() diff --git a/lib/include/prjxray/segbits_file_reader.h b/lib/include/prjxray/segbits_file_reader.h new file mode 100644 index 00000000..277eba1c --- /dev/null +++ b/lib/include/prjxray/segbits_file_reader.h @@ -0,0 +1,67 @@ +#ifndef PRJXRAY_LIB_SEGBITS_FILE_READER_H +#define PRJXRAY_LIB_SEGBITS_FILE_READER_H + +#include +#include + +#include +#include + +namespace prjxray { + +class SegbitsFileReader { + public: + class value_type { + public: + absl::string_view tag() const { return tag_; } + absl::string_view bit() const { return bit_; } + + private: + friend SegbitsFileReader; + + value_type(const absl::string_view &view); + + absl::string_view tag_; + absl::string_view bit_; + }; + + class iterator + : public std::iterator { + public: + iterator& operator++(); + + bool operator==(iterator other) const { + return view_ == other.view_; } + bool operator!=(iterator other) const { + return !(*this == other);} + + const value_type& operator*() const { return value_; } + const value_type* operator->() const { return &value_; } + + protected: + explicit iterator(absl::string_view view) + : view_(view), value_(view) {} + + private: + friend SegbitsFileReader; + + absl::string_view view_; + value_type value_; + }; + + static std::unique_ptr InitWithFile( + const std::string &path); + + iterator begin(); + iterator end(); + + private: + SegbitsFileReader(std::unique_ptr &&mapped_file) + : mapped_file_(std::move(mapped_file)) {}; + + std::unique_ptr mapped_file_; +}; + +} // namespace prjxray + +#endif // PRJXRAY_LIB_SEGBITS_FILE_READER_H diff --git a/lib/segbits_file_reader.cc b/lib/segbits_file_reader.cc new file mode 100644 index 00000000..96e06ed9 --- /dev/null +++ b/lib/segbits_file_reader.cc @@ -0,0 +1,59 @@ +#include + +namespace prjxray { + +std::unique_ptr SegbitsFileReader::InitWithFile( + const std::string &path) { + + auto mapped_file = MemoryMappedFile::InitWithFile(path); + if (!mapped_file) return nullptr; + + return std::unique_ptr( + new SegbitsFileReader(std::move(mapped_file))); +} + +SegbitsFileReader::iterator SegbitsFileReader::begin() { + return iterator(absl::string_view( + static_cast(mapped_file_->data()), + mapped_file_->size())); +} + +SegbitsFileReader::iterator SegbitsFileReader::end() { + return iterator(absl::string_view()); +} + +SegbitsFileReader::value_type::value_type(const absl::string_view &view) { + size_t separator_start = view.find_first_of(" \t"); + if (separator_start == absl::string_view::npos) { + tag_ = view; + bit_ = absl::string_view(); + return; + } + + size_t bit_start = view.find_first_not_of(" \t", separator_start); + size_t newline = view.find('\n', bit_start); + if (newline == absl::string_view::npos) { + tag_ = view.substr(0, separator_start); + bit_ = view.substr(bit_start); + return; + } + + size_t bit_len = newline - bit_start; + tag_ = view.substr(0, separator_start); + bit_ = view.substr(bit_start, bit_len); + return; +} + +SegbitsFileReader::iterator& SegbitsFileReader::iterator::operator++() { + size_t newline = view_.find('\n'); + if (newline == absl::string_view::npos) { + view_ = absl::string_view(); + } + + view_.remove_prefix(newline + 1); + value_ = value_type(view_); + return *this; +} + + +} // namespace prjxray diff --git a/lib/segbits_file_reader_test.cc b/lib/segbits_file_reader_test.cc new file mode 100644 index 00000000..35764f24 --- /dev/null +++ b/lib/segbits_file_reader_test.cc @@ -0,0 +1,82 @@ +#include + +#include +#include + +#include + +TEST(SegbitsFileReaderTest, NonExistantFileReturnsNull) { + EXPECT_FALSE(prjxray::SegbitsFileReader::InitWithFile("does_not_exist")); +} + +TEST(SegbitsFileReaderTest, ZeroLengthFileYieldsNoItems) { + auto segbits_reader = + prjxray::SegbitsFileReader::InitWithFile("empty_file"); + ASSERT_TRUE(segbits_reader); + + EXPECT_EQ(segbits_reader->begin(), segbits_reader->end()); +} + +TEST(SegbitsFileReaderTest, FileWithOneEntry) { + auto segbits_reader = + prjxray::SegbitsFileReader::InitWithFile("one_entry.segbits"); + ASSERT_TRUE(segbits_reader); + + auto begin_iter = segbits_reader->begin(); + EXPECT_EQ(begin_iter->tag(), "CLBLL_L.SLICEL_X0.A5FF.ZINI"); + EXPECT_EQ(begin_iter->bit(), "31_06"); + + EXPECT_EQ(++begin_iter, segbits_reader->end()); +} + +TEST(SegbitsFileReaderTest, FileWithOneEntryWithEmptyTag) { + auto segbits_reader = prjxray::SegbitsFileReader::InitWithFile( + "one_entry_empty_tag.segbits"); + ASSERT_TRUE(segbits_reader); + + auto begin_iter = segbits_reader->begin(); + EXPECT_EQ(begin_iter->tag(), ""); + EXPECT_EQ(begin_iter->bit(), "31_06"); + + EXPECT_EQ(++begin_iter, segbits_reader->end()); +} + +TEST(SegbitsFileReaderTest, FileWithOneEntryMissingBit) { + auto segbits_reader = prjxray::SegbitsFileReader::InitWithFile( + "one_entry_missing_bit.segbits"); + ASSERT_TRUE(segbits_reader); + + auto begin_iter = segbits_reader->begin(); + EXPECT_EQ(begin_iter->tag(), "CLBLL_L.SLICEL_X0.A5FF.ZINI"); + EXPECT_EQ(begin_iter->bit(), ""); + + EXPECT_EQ(++begin_iter, segbits_reader->end()); +} + +TEST(SegbitsFileReaderTest, FileWithOneEntryWithExtraWhitespace) { + auto segbits_reader = prjxray::SegbitsFileReader::InitWithFile( + "one_entry_extra_whitespace.segbits"); + ASSERT_TRUE(segbits_reader); + + auto begin_iter = segbits_reader->begin(); + EXPECT_EQ(begin_iter->tag(), "CLBLL_L.SLICEL_X0.A5FF.ZINI"); + EXPECT_EQ(begin_iter->bit(), "31_06"); + + EXPECT_EQ(++begin_iter, segbits_reader->end()); +} + +TEST(SegbitsFileReaderTest, FileWithTwoEntries) { + auto segbits_reader = prjxray::SegbitsFileReader::InitWithFile( + "two_entries.segbits"); + ASSERT_TRUE(segbits_reader); + + auto iter = segbits_reader->begin(); + EXPECT_EQ(iter->tag(), "CLBLL_L.SLICEL_X0.A5FF.ZINI"); + EXPECT_EQ(iter->bit(), "31_06"); + + ++iter; + EXPECT_EQ(iter->tag(), "CLBLL_L.SLICEL_X0.AFF.ZINI"); + EXPECT_EQ(iter->bit(), "31_03"); + + EXPECT_EQ(++iter, segbits_reader->end()); +} diff --git a/lib/test_data/one_entry.segbits b/lib/test_data/one_entry.segbits new file mode 100644 index 00000000..eee8c450 --- /dev/null +++ b/lib/test_data/one_entry.segbits @@ -0,0 +1 @@ +CLBLL_L.SLICEL_X0.A5FF.ZINI 31_06 diff --git a/lib/test_data/one_entry_empty_tag.segbits b/lib/test_data/one_entry_empty_tag.segbits new file mode 100644 index 00000000..cc99b3fe --- /dev/null +++ b/lib/test_data/one_entry_empty_tag.segbits @@ -0,0 +1 @@ + 31_06 diff --git a/lib/test_data/one_entry_extra_whitespace.segbits b/lib/test_data/one_entry_extra_whitespace.segbits new file mode 100644 index 00000000..12d1135b --- /dev/null +++ b/lib/test_data/one_entry_extra_whitespace.segbits @@ -0,0 +1 @@ +CLBLL_L.SLICEL_X0.A5FF.ZINI 31_06 diff --git a/lib/test_data/one_entry_missing_bit.segbits b/lib/test_data/one_entry_missing_bit.segbits new file mode 100644 index 00000000..d1d77fe2 --- /dev/null +++ b/lib/test_data/one_entry_missing_bit.segbits @@ -0,0 +1 @@ +CLBLL_L.SLICEL_X0.A5FF.ZINI diff --git a/lib/test_data/two_entries.segbits b/lib/test_data/two_entries.segbits new file mode 100644 index 00000000..cc6ca46f --- /dev/null +++ b/lib/test_data/two_entries.segbits @@ -0,0 +1,2 @@ +CLBLL_L.SLICEL_X0.A5FF.ZINI 31_06 +CLBLL_L.SLICEL_X0.AFF.ZINI 31_03