// Copyright (C) 2016-2017 Thomas Fussell // Copyright (C) 2002-2007 Ariya Hidayat (ariya@kde.org). // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. // IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT // NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #include #include #include #include #include #include namespace { using namespace xlnt::detail; int compare_keys(const std::string &left, const std::string &right) { auto to_lower = [](std::string s) { static const auto locale = std::locale(); std::use_facet>(locale).tolower(&s[0], &s[0] + s.size()); return s; }; return to_lower(left).compare(to_lower(right)); } std::vector split_path(const std::string &path) { auto split = std::vector(); auto current = path.find('/'); auto prev = std::size_t(0); while (current != std::string::npos) { split.push_back(path.substr(prev, current - prev)); prev = current + 1; current = path.find('/', prev); } split.push_back(path.substr(prev)); return split; } std::string join_path(const std::vector &path) { auto joined = std::string(); for (auto part : path) { joined.append(part); joined.push_back('/'); } return joined; } const sector_id FreeSector = -1; const sector_id EndOfChain = -2; const sector_id SATSector = -3; //const sector_id MSATSector = -4; const directory_id End = -1; } // namespace namespace xlnt { namespace detail { /// /// Allows a std::vector to be read through a std::istream. /// class compound_document_istreambuf : public std::streambuf { using int_type = std::streambuf::int_type; public: compound_document_istreambuf(const compound_document_entry &entry, compound_document &document) : entry_(entry), document_(document), position_(0), sector_writer_(current_sector_) { } compound_document_istreambuf(const compound_document_istreambuf &) = delete; compound_document_istreambuf &operator=(const compound_document_istreambuf &) = delete; private: int_type underflow() { if (position_ == entry_.size) { return traits_type::eof(); } sector_writer_.reset(); if (entry_.size < document_.header_.threshold) { document_.read_short_sector_chain(entry_.start, sector_writer_, sector_id(position_ / document_.short_sector_size()), 1); return current_sector_[position_ % document_.short_sector_size()]; } else { document_.read_sector_chain(entry_.start, sector_writer_, sector_id(position_ / document_.sector_size()), 1); return current_sector_[position_ % document_.sector_size()]; } } int_type uflow() { auto result = underflow(); ++position_; return result; } std::streamsize showmanyc() { if (position_ == entry_.size) { return static_cast(-1); } return static_cast(entry_.size - position_); } std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode) { if (way == std::ios_base::beg) { position_ = 0; } else if (way == std::ios_base::end) { position_ = entry_.size; } if (off < 0) { if (static_cast(-off) > position_) { position_ = 0; return static_cast(-1); } else { position_ -= static_cast(-off); } } else if (off > 0) { if (static_cast(off) + position_ > entry_.size) { position_ = entry_.size; return static_cast(-1); } else { position_ += static_cast(off); } } return static_cast(position_); } std::streampos seekpos(std::streampos sp, std::ios_base::openmode) { if (sp < 0) { position_ = 0; } else if (static_cast(sp) > entry_.size) { position_ = entry_.size; } else { position_ = static_cast(sp); } return static_cast(position_); } private: const compound_document_entry &entry_; compound_document &document_; binary_writer sector_writer_; std::vector current_sector_; std::size_t position_; }; /// /// Allows a std::vector to be written through a std::ostream. /// class compound_document_ostreambuf : public std::streambuf { using int_type = std::streambuf::int_type; public: compound_document_ostreambuf(compound_document_entry &entry, compound_document &document) : entry_(entry), document_(document), position_(0), sector_reader_(current_sector_), current_sector_(sector_size(), 0), chain_(document_.follow_chain(entry_.start, table())) { } compound_document_ostreambuf(const compound_document_ostreambuf &) = delete; compound_document_ostreambuf &operator=(const compound_document_ostreambuf &) = delete; virtual ~compound_document_ostreambuf() { if (position_ % 64 != 0) { write_sector(); } } private: bool short_stream() { return entry_.size < document_.header_.threshold; } sector_chain &table() { return short_stream() ? document_.ssat_ : document_.sat_; } std::size_t sector_size() { return short_stream() ? document_.short_sector_size() : document_.short_sector_size(); } void write_sector() { if (short_stream()) { auto next_sector = document_.allocate_short_sector(); document_.ssat_[chain_.back()] = next_sector; chain_.push_back(next_sector); document_.write_short_sector(sector_reader_, next_sector); } else { auto next_sector = document_.allocate_sector(); document_.sat_[chain_.back()] = next_sector; chain_.push_back(next_sector); document_.write_sector(sector_reader_, next_sector); } } int_type overflow(int_type c = traits_type::eof()) { auto value = static_cast(c); if (c != traits_type::eof()) { current_sector_[position_ % sector_size()] = value; } if (entry_.start < 0) { entry_.start = entry_.size == document_.header_.threshold ? document_.allocate_sector() : document_.allocate_short_sector(); chain_.push_back(entry_.start); } if (position_ % 64 == 0 && position_ > 0) { write_sector(); std::fill(current_sector_.begin(), current_sector_.end(), byte(0)); } if (c != traits_type::eof()) { ++position_; auto previous_size = entry_.size; entry_.size = std::max(entry_.size, static_cast(position_)); if (entry_.size >= document_.header_.threshold && previous_size < document_.header_.threshold) { convert_to_long_stream(); } return traits_type::to_int_type(static_cast(value)); } else { return traits_type::eof(); } } void convert_to_long_stream() { const auto sectors_per_sector = document_.sector_size() / document_.short_sector_size(); current_sector_.resize(sector_size(), 0); std::fill(current_sector_.begin(), current_sector_.end(), byte(0)); auto sector_writer = binary_writer(current_sector_); auto index = std::size_t(0); auto long_chain = sector_chain(); entry_.start = document_.allocate_sector(); long_chain.push_back(entry_.start); for (auto link : chain_) { document_.read_short_sector(link, sector_writer); document_.header_.num_short_sectors--; document_.ssat_[link] = FreeSector; if (index % sectors_per_sector == 0 && index > 0) { document_.write_sector(sector_reader_, long_chain.back()); auto next_sector = document_.allocate_sector(); document_.sat_[long_chain.back()] = next_sector; long_chain.push_back(next_sector); } } if (index % sectors_per_sector != 0) { document_.write_sector(sector_reader_, long_chain.back()); } index = 0; auto previous = sector_id(0); for (auto link : document_.follow_chain(document_.entries_[0].start, document_.sat_)) { auto ssat_index_start = document_.ssat_.begin() + index * sectors_per_sector; auto ssat_index_end = document_.ssat_.begin() + (index + 1) * sectors_per_sector; if (std::size_t(std::count(ssat_index_start, ssat_index_end, FreeSector)) == sectors_per_sector) { if (index > 0) { document_.sat_[previous] = document_.sat_[link]; } else { document_.entries_[0].start = document_.sat_[link]; } document_.sat_[link] = FreeSector; } previous = link; index++; } if (document_.header_.num_short_sectors == 0) { document_.entries_[0].start = EndOfChain; } // TODO: deallocate short sectors here chain_ = long_chain; } std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode) { if (way == std::ios_base::beg) { position_ = 0; } else if (way == std::ios_base::end) { position_ = entry_.size; } if (off < 0) { if (static_cast(-off) > position_) { position_ = 0; return static_cast(-1); } else { position_ -= static_cast(-off); } } else if (off > 0) { if (static_cast(off) + position_ > entry_.size) { position_ = entry_.size; return static_cast(-1); } else { position_ += static_cast(off); } } return static_cast(position_); } std::streampos seekpos(std::streampos sp, std::ios_base::openmode) { if (sp < 0) { position_ = 0; } else if (static_cast(sp) > entry_.size) { position_ = entry_.size; } else { position_ = static_cast(sp); } return static_cast(position_); } private: compound_document_entry &entry_; compound_document &document_; binary_reader sector_reader_; std::vector current_sector_; std::size_t position_; sector_chain chain_; }; compound_document::compound_document(std::ostream &out) : out_(&out), stream_in_(nullptr), stream_out_(nullptr) { write_header(); insert_entry("/Root Entry", compound_document_entry::entry_type::RootStorage); } compound_document::compound_document(std::istream &in) : in_(&in), stream_in_(nullptr), stream_out_(nullptr) { read_header(); read_msat(); read_sat(); read_ssat(); read_directory(); } compound_document::~compound_document() { close(); } void compound_document::close() { stream_out_buffer_.reset(nullptr); } std::size_t compound_document::sector_size() { return static_cast(1) << header_.sector_size_power; } std::size_t compound_document::short_sector_size() { return static_cast(1) << header_.short_sector_size_power; } std::istream &compound_document::open_read_stream(const std::string &name) { const auto entry_id = find_entry(name, compound_document_entry::entry_type::UserStream); const auto &entry = entries_.at(entry_id); stream_in_buffer_.reset(new compound_document_istreambuf(entry, *this)); stream_in_.rdbuf(stream_in_buffer_.get()); return stream_in_; } std::ostream &compound_document::open_write_stream(const std::string &name) { auto entry_id = contains_entry(name, compound_document_entry::entry_type::UserStream) ? find_entry(name, compound_document_entry::entry_type::UserStream) : insert_entry(name, compound_document_entry::entry_type::UserStream); auto &entry = entries_.at(entry_id); stream_out_buffer_.reset(new compound_document_ostreambuf(entry, *this)); stream_out_.rdbuf(stream_out_buffer_.get()); return stream_out_; } template void compound_document::write_sector(binary_reader &reader, sector_id id) { out_->seekp(sector_data_start() + sector_size() * id); out_->write(reinterpret_cast(reader.data() + reader.offset()), std::min(sector_size(), reader.bytes() - reader.offset())); } template void compound_document::write_short_sector(binary_reader &reader, sector_id id) { auto chain = follow_chain(entries_[0].start, sat_); auto sector_id = chain[id / (sector_size() / short_sector_size())]; auto sector_offset = id % (sector_size() / short_sector_size()) * short_sector_size(); out_->seekp(sector_data_start() + sector_size() * sector_id + sector_offset); out_->write(reinterpret_cast(reader.data() + reader.offset()), std::min(short_sector_size(), reader.bytes() - reader.offset())); } template void compound_document::read_sector(sector_id id, binary_writer &writer) { in_->seekg(sector_data_start() + sector_size() * id); std::vector sector(sector_size(), 0); in_->read(reinterpret_cast(sector.data()), sector_size()); writer.append(sector); } template void compound_document::read_sector_chain(sector_id start, binary_writer &writer) { for (auto link : follow_chain(start, sat_)) { read_sector(link, writer); } } template void compound_document::read_sector_chain(sector_id start, binary_writer &writer, sector_id offset, std::size_t count) { auto chain = follow_chain(start, sat_); for (auto i = std::size_t(0); i < count; ++i) { read_sector(chain[offset + i], writer); } } template void compound_document::read_short_sector(sector_id id, binary_writer &writer) { const auto container_chain = follow_chain(entries_[0].start, sat_); auto container = std::vector(); auto container_writer = binary_writer(container); for (auto sector : container_chain) { read_sector(sector, container_writer); } auto container_reader = binary_reader(container); container_reader.offset(id * short_sector_size()); writer.append(container_reader, short_sector_size()); } template void compound_document::read_short_sector_chain(sector_id start, binary_writer &writer) { for (auto link : follow_chain(start, ssat_)) { read_short_sector(link, writer); } } template void compound_document::read_short_sector_chain(sector_id start, binary_writer &writer, sector_id offset, std::size_t count) { auto chain = follow_chain(start, ssat_); for (auto i = std::size_t(0); i < count; ++i) { read_short_sector(chain[offset + i], writer); } } sector_id compound_document::allocate_sector() { const auto sectors_per_sector = sector_size() / sizeof(sector_id); auto next_free_iter = std::find(sat_.begin(), sat_.end(), FreeSector); if (next_free_iter == sat_.end()) { auto next_msat_index = header_.num_msat_sectors; auto new_sat_sector_id = sector_id(sat_.size()); msat_.push_back(new_sat_sector_id); header_.msat[msat_.size() - 1] = new_sat_sector_id; sat_.resize(sat_.size() + sectors_per_sector, FreeSector); sat_[new_sat_sector_id] = SATSector; auto sat_reader = binary_reader(sat_); sat_reader.offset(next_msat_index * sectors_per_sector); write_sector(sat_reader, new_sat_sector_id); next_free_iter = std::find(sat_.begin(), sat_.end(), FreeSector); } auto next_free = sector_id(next_free_iter - sat_.begin()); sat_[next_free] = EndOfChain; write_sat(); auto empty_sector = std::vector(sector_size()); auto empty_sector_reader = binary_reader(empty_sector); write_sector(empty_sector_reader, next_free); return next_free; } sector_chain compound_document::allocate_sectors(std::size_t count) { if (count == std::size_t(0)) return {}; auto chain = sector_chain(); auto current = allocate_sector(); for (auto i = std::size_t(1); i < count; ++i) { chain.push_back(current); auto next = allocate_sector(); sat_[current] = next; current = next; } return chain; } sector_chain compound_document::follow_chain(sector_id start, const sector_chain &table) { auto chain = sector_chain(); auto current = start; while (current >= 0) { chain.push_back(current); current = table[current]; } return chain; } sector_chain compound_document::allocate_short_sectors(std::size_t count) { if (count == std::size_t(0)) return {}; auto chain = sector_chain(); auto current = allocate_short_sector(); for (auto i = std::size_t(1); i < count; ++i) { chain.push_back(current); auto next = allocate_short_sector(); ssat_[current] = next; current = next; } chain.push_back(current); return chain; } sector_id compound_document::allocate_short_sector() { const auto sectors_per_sector = sector_size() / sizeof(sector_id); auto next_free_iter = std::find(ssat_.begin(), ssat_.end(), FreeSector); if (next_free_iter == ssat_.end()) { auto new_ssat_sector_id = allocate_sector(); ++header_.num_short_sectors; if (header_.ssat_start < 0) { header_.ssat_start = new_ssat_sector_id; } else { auto ssat_chain = follow_chain(header_.ssat_start, sat_); sat_[ssat_chain.back()] = new_ssat_sector_id; } write_header(); auto old_size = ssat_.size(); ssat_.resize(old_size + sectors_per_sector, FreeSector); auto ssat_reader = binary_reader(ssat_); ssat_reader.offset(old_size / sectors_per_sector); write_sector(ssat_reader, new_ssat_sector_id); next_free_iter = std::find(ssat_.begin(), ssat_.end(), FreeSector); } auto next_free = sector_id(next_free_iter - ssat_.begin()); ssat_[next_free] = EndOfChain; write_ssat(); const auto short_sectors_per_sector = sector_size() / short_sector_size(); const auto required_container_sectors = std::size_t(next_free / short_sectors_per_sector + 1); if (required_container_sectors > 0) { if (entries_[0].start < 0) { entries_[0].start = allocate_sector(); write_entry(0); } auto container_chain = follow_chain(entries_[0].start, sat_); if (required_container_sectors > container_chain.size()) { sat_[container_chain.back()] = allocate_sector(); write_sat(); } } return next_free; } directory_id compound_document::next_empty_entry() { auto entry_id = directory_id(0); for (; entry_id < directory_id(entries_.size()); ++entry_id) { auto &entry = entries_[entry_id]; if (entry.type == compound_document_entry::entry_type::Empty) { return entry_id; } } // entry_id is now equal to entries_.size() if (header_.directory_start < 0) { header_.directory_start = allocate_sector(); } else { auto directory_chain = follow_chain(header_.directory_start, sat_); sat_[directory_chain.back()] = allocate_sector(); } const auto entries_per_sector = sector_size() / sizeof(compound_document_entry); for (auto i = std::size_t(0); i < entries_per_sector; ++i) { auto empty_entry = compound_document_entry(); empty_entry.type = compound_document_entry::entry_type::Empty; entries_.push_back(empty_entry); write_entry(entry_id + directory_id(i)); } return entry_id; } directory_id compound_document::insert_entry( const std::string &name, compound_document_entry::entry_type type) { auto entry_id = next_empty_entry(); auto &entry = entries_[entry_id]; auto parent_id = directory_id(0); auto split = split_path(name); auto filename = split.back(); split.pop_back(); if (split.size() > 1) { parent_id = find_entry(join_path(split), compound_document_entry::entry_type::UserStorage); parent_storage_[entry_id] = parent_id; } entry.name(filename); entry.type = type; tree_insert(entry_id, parent_id); write_directory(); return entry_id; } std::size_t compound_document::sector_data_start() { return sizeof(compound_document_header); } bool compound_document::contains_entry(const std::string &path, compound_document_entry::entry_type type) { return find_entry(path, type) >= 0; } directory_id compound_document::find_entry(const std::string &name, compound_document_entry::entry_type type) { if (type == compound_document_entry::entry_type::RootStorage && (name == "/" || name == "/Root Entry")) return 0; auto entry_id = directory_id(0); for (auto &entry : entries_) { if (entry.type == type && tree_path(entry_id) == name) { return entry_id; } ++entry_id; } return End; } void compound_document::print_directory() { auto entry_id = directory_id(0); for (auto &entry : entries_) { if (entry.type == compound_document_entry::entry_type::UserStream) { std::cout << tree_path(entry_id) << std::endl; } ++entry_id; } } void compound_document::write_directory() { for (auto entry_id = std::size_t(0); entry_id < entries_.size(); ++entry_id) { write_entry(directory_id(entry_id++)); } } void compound_document::read_directory() { const auto entries_per_sector = sector_size() / sizeof(compound_document_entry); const auto num_entries = follow_chain(header_.directory_start, sat_).size() * entries_per_sector; for (auto entry_id = std::size_t(0); entry_id < num_entries; ++entry_id) { entries_.push_back(compound_document_entry()); read_entry(directory_id(entry_id)); } auto stack = std::vector(); auto storage_siblings = std::vector(); auto stream_siblings = std::vector(); auto directory_stack = std::vector(); directory_stack.push_back(directory_id(0)); while (!directory_stack.empty()) { auto current_storage_id = directory_stack.back(); directory_stack.pop_back(); if (tree_child(current_storage_id) < 0) continue; auto storage_stack = std::vector(); auto storage_root_id = tree_child(current_storage_id); parent_[storage_root_id] = End; storage_stack.push_back(storage_root_id); while (!storage_stack.empty()) { auto current_entry_id = storage_stack.back(); auto current_entry = entries_[current_entry_id]; storage_stack.pop_back(); parent_storage_[current_entry_id] = current_storage_id; if (current_entry.type == compound_document_entry::entry_type::UserStorage) { directory_stack.push_back(current_entry_id); } if (tree_left(current_entry_id) >= 0) { storage_stack.push_back(tree_left(current_entry_id)); tree_parent(tree_left(current_entry_id)) = current_entry_id; } if (tree_right(current_entry_id) >= 0) { storage_stack.push_back(tree_right(current_entry_id)); tree_parent(tree_right(current_entry_id)) = current_entry_id; } } } } void compound_document::tree_insert(directory_id new_id, directory_id storage_id) { using entry_color = compound_document_entry::entry_color; parent_storage_[new_id] = storage_id; tree_left(new_id) = End; tree_right(new_id) = End; if (tree_root(new_id) == End) { if (new_id != 0) { tree_root(new_id) = new_id; } tree_color(new_id) = entry_color::Black; tree_parent(new_id) = End; return; } // normal tree insert // (will probably unbalance the tree, fix after) auto x = tree_root(new_id); auto y = End; while (x >= 0) { y = x; if (compare_keys(tree_key(new_id), tree_key(x)) > 0) { x = tree_right(x); } else { x = tree_left(x); } } tree_parent(new_id) = y; if (compare_keys(tree_key(new_id), tree_key(y)) > 0) { tree_right(y) = new_id; } else { tree_left(y) = new_id; } tree_insert_fixup(new_id); } std::string compound_document::tree_path(directory_id id) { auto storage_id = parent_storage_[id]; auto result = std::vector(); while (storage_id > 0) { storage_id = parent_storage_[storage_id]; result.push_back(entries_[storage_id].name()); } return "/" + join_path(result) + entries_[id].name(); } void compound_document::tree_rotate_left(directory_id x) { auto y = tree_right(x); // turn y's left subtree into x's right subtree tree_right(x) = tree_left(y); if (tree_left(y) != End) { tree_parent(tree_left(y)) = x; } // link x's parent to y tree_parent(y) = tree_parent(x); if (tree_parent(x) == End) { tree_root(x) = y; } else if (x == tree_left(tree_parent(x))) { tree_left(tree_parent(x)) = y; } else { tree_right(tree_parent(x)) = y; } // put x on y's left tree_left(y) = x; tree_parent(x) = y; } void compound_document::tree_rotate_right(directory_id y) { auto x = tree_left(y); // turn x's right subtree into y's left subtree tree_left(y) = tree_right(x); if (tree_right(x) != End) { tree_parent(tree_right(x)) = y; } // link y's parent to x tree_parent(x) = tree_parent(y); if (tree_parent(y) == End) { tree_root(y) = x; } else if (y == tree_left(tree_parent(y))) { tree_left(tree_parent(y)) = x; } else { tree_right(tree_parent(y)) = x; } // put y on x's right tree_right(x) = y; tree_parent(y) = x; } void compound_document::tree_insert_fixup(directory_id x) { using entry_color = compound_document_entry::entry_color; tree_color(x) = entry_color::Red; while (x != tree_root(x) && tree_color(tree_parent(x)) == entry_color::Red) { if (tree_parent(x) == tree_left(tree_parent(tree_parent(x)))) { auto y = tree_right(tree_parent(tree_parent(x))); if (y >= 0 && tree_color(y) == entry_color::Red) { // case 1 tree_color(tree_parent(x)) = entry_color::Black; tree_color(y) = entry_color::Black; tree_color(tree_parent(tree_parent(x))) = entry_color::Red; x = tree_parent(tree_parent(x)); } else { if (x == tree_right(tree_parent(x))) { // case 2 x = tree_parent(x); tree_rotate_left(x); } // case 3 tree_color(tree_parent(x)) = entry_color::Black; tree_color(tree_parent(tree_parent(x))) = entry_color::Red; tree_rotate_right(tree_parent(tree_parent(x))); } } else // same as above with left and right switched { auto y = tree_left(tree_parent(tree_parent(x))); if (y >= 0 && tree_color(y) == entry_color::Red) { //case 1 tree_color(tree_parent(x)) = entry_color::Black; tree_color(y) = entry_color::Black; tree_color(tree_parent(tree_parent(x))) = entry_color::Red; x = tree_parent(tree_parent(x)); } else { if (x == tree_left(tree_parent(x))) { // case 2 x = tree_parent(x); tree_rotate_right(x); } // case 3 tree_color(tree_parent(x)) = entry_color::Black; tree_color(tree_parent(tree_parent(x))) = entry_color::Red; tree_rotate_left(tree_parent(tree_parent(x))); } } } tree_color(tree_root(x)) = entry_color::Black; } directory_id &compound_document::tree_left(directory_id id) { return entries_[id].prev; } directory_id &compound_document::tree_right(directory_id id) { return entries_[id].next; } directory_id &compound_document::tree_parent(directory_id id) { return parent_[id]; } directory_id &compound_document::tree_root(directory_id id) { return tree_child(parent_storage_[id]); } directory_id &compound_document::tree_child(directory_id id) { return entries_[id].child; } std::string compound_document::tree_key(directory_id id) { return entries_[id].name(); } compound_document_entry::entry_color &compound_document::tree_color(directory_id id) { return entries_[id].color; } void compound_document::read_header() { in_->seekg(0, std::ios::beg); in_->read(reinterpret_cast(&header_), sizeof(compound_document_header)); } void compound_document::read_msat() { msat_.clear(); auto msat_sector = header_.extra_msat_start; auto msat_writer = binary_writer(msat_); for (auto i = std::uint32_t(0); i < header_.num_msat_sectors; ++i) { if (i < std::uint32_t(109)) { msat_writer.write(header_.msat[i]); } else { read_sector(msat_sector, msat_writer); msat_sector = msat_.back(); msat_.pop_back(); } } } void compound_document::read_sat() { sat_.clear(); auto sat_writer = binary_writer(sat_); for (auto msat_sector : msat_) { read_sector(msat_sector, sat_writer); } } void compound_document::read_ssat() { ssat_.clear(); for (auto ssat_sector : follow_chain(header_.ssat_start, sat_)) { auto sector = std::vector(); auto sector_writer = binary_writer(sector); read_sector(ssat_sector, sector_writer); std::copy(sector.begin(), sector.end(), std::back_inserter(ssat_)); } } void compound_document::read_entry(directory_id id) { const auto directory_chain = follow_chain(header_.directory_start, sat_); const auto entries_per_sector = sector_size() / sizeof(compound_document_entry); const auto directory_sector = directory_chain[id / entries_per_sector]; const auto offset = sector_size() * directory_sector + ((id % entries_per_sector) * sizeof(compound_document_entry)); in_->seekg(sector_data_start() + offset, std::ios::beg); in_->read(reinterpret_cast(&entries_[id]), sizeof(compound_document_entry)); } void compound_document::write_header() { out_->seekp(0, std::ios::beg); out_->write(reinterpret_cast(&header_), sizeof(compound_document_header)); } void compound_document::write_msat() { auto msat_sector = header_.extra_msat_start; for (auto i = std::uint32_t(0); i < header_.num_msat_sectors; ++i) { if (i < std::uint32_t(109)) { header_.msat[i] = msat_[i]; } else { auto sector = std::vector(); auto sector_writer = binary_writer(sector); read_sector(msat_sector, sector_writer); msat_sector = sector.back(); sector.pop_back(); std::copy(sector.begin(), sector.end(), std::back_inserter(msat_)); } } } void compound_document::write_sat() { auto sector_reader = binary_reader(sat_); for (auto sat_sector : msat_) { write_sector(sector_reader, sat_sector); } } void compound_document::write_ssat() { auto sector_reader = binary_reader(ssat_); for (auto ssat_sector : follow_chain(header_.ssat_start, sat_)) { write_sector(sector_reader, ssat_sector); } } void compound_document::write_entry(directory_id id) { const auto directory_chain = follow_chain(header_.directory_start, sat_); const auto entries_per_sector = sector_size() / sizeof(compound_document_entry); const auto directory_sector = directory_chain[id / entries_per_sector]; const auto offset = sector_size() * directory_sector + ((id % entries_per_sector) * sizeof(compound_document_entry)); out_->seekp(offset, std::ios::beg); out_->write(reinterpret_cast(&entries_[id]), sizeof(compound_document_entry)); } } // namespace detail } // namespace xlnt