mirror of
https://github.com/tfussell/xlnt.git
synced 2024-03-22 13:11:17 +08:00
work on short streams
This commit is contained in:
parent
a90b32f386
commit
6334907de7
|
@ -26,6 +26,7 @@
|
|||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -38,42 +39,26 @@ namespace {
|
|||
|
||||
using namespace xlnt::detail;
|
||||
|
||||
int compare_keys(const std::u16string &left, const std::u16string &right)
|
||||
int compare_keys(const std::string &left, const std::string &right)
|
||||
{
|
||||
auto to_lower = [](std::u16string s)
|
||||
auto to_lower = [](std::string s)
|
||||
{
|
||||
static const auto locale = std::locale();
|
||||
std::use_facet<std::ctype<char16_t>>(locale).tolower(&s[0], &s[0] + s.size());
|
||||
std::use_facet<std::ctype<char>>(locale).tolower(&s[0], &s[0] + s.size());
|
||||
return s;
|
||||
};
|
||||
|
||||
return to_lower(left).compare(to_lower(right));
|
||||
}
|
||||
|
||||
std::vector<std::u16string> split_path(const std::u16string &path_string)
|
||||
std::string join_path(const std::vector<std::string> &path)
|
||||
{
|
||||
auto sep = path_string.find(u'/');
|
||||
auto prev = std::size_t(0);
|
||||
auto split = std::vector<std::u16string>();
|
||||
|
||||
while (sep != std::u16string::npos)
|
||||
{
|
||||
split.push_back(path_string.substr(prev, sep - prev));
|
||||
prev = sep;
|
||||
sep = path_string.find(u'/');
|
||||
}
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
std::u16string join_path(const std::vector<std::u16string> &path)
|
||||
{
|
||||
auto joined = std::u16string();
|
||||
auto joined = std::string();
|
||||
|
||||
for (auto part : path)
|
||||
{
|
||||
joined.append(part);
|
||||
joined.push_back(u'/');
|
||||
joined.push_back('/');
|
||||
}
|
||||
|
||||
return joined;
|
||||
|
@ -82,7 +67,7 @@ std::u16string join_path(const std::vector<std::u16string> &path)
|
|||
const sector_id FreeSector = -1;
|
||||
const sector_id EndOfChain = -2;
|
||||
const sector_id SATSector = -3;
|
||||
const sector_id MSATSector = -4;
|
||||
//const sector_id MSATSector = -4;
|
||||
|
||||
const directory_id End = -1;
|
||||
|
||||
|
@ -95,15 +80,72 @@ compound_document::compound_document(std::vector<std::uint8_t> &data)
|
|||
: writer_(new binary_writer<byte>(data)),
|
||||
reader_(new binary_reader<byte>(data))
|
||||
{
|
||||
header().msat.fill(FreeSector);
|
||||
header().directory_start = allocate_sector();
|
||||
header_.msat.fill(FreeSector);
|
||||
writer_->offset(0);
|
||||
writer_->write(header_);
|
||||
|
||||
insert_entry(u"Root Entry", compound_document_entry::entry_type::RootStorage);
|
||||
header_.directory_start = allocate_sector();
|
||||
writer_->offset(0);
|
||||
writer_->write(header_);
|
||||
|
||||
insert_entry("Root Entry", compound_document_entry::entry_type::RootStorage);
|
||||
}
|
||||
|
||||
compound_document::compound_document(const std::vector<std::uint8_t> &data)
|
||||
: reader_(new binary_reader<byte>(data))
|
||||
: writer_(nullptr),
|
||||
reader_(new binary_reader<byte>(data))
|
||||
{
|
||||
header_ = reader_->read<compound_document_header>();
|
||||
|
||||
// read msat
|
||||
auto current = header_.extra_msat_start;
|
||||
|
||||
for (auto i = std::size_t(0); i < header_.num_msat_sectors; ++i)
|
||||
{
|
||||
if (i < 109)
|
||||
{
|
||||
msat_.push_back(header_.msat[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto extra_msat_sector = std::vector<sector_id>();
|
||||
auto extra_msat_sector_writer = binary_writer<sector_id>(extra_msat_sector);
|
||||
|
||||
read_sector(current, extra_msat_sector_writer);
|
||||
|
||||
std::copy(extra_msat_sector.begin(),
|
||||
extra_msat_sector.end() - 1,
|
||||
std::back_inserter(msat_));
|
||||
current = extra_msat_sector.back();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto sat_sector_id : msat_)
|
||||
{
|
||||
if (sat_sector_id < 0) continue;
|
||||
|
||||
auto sat_sector = std::vector<sector_id>();
|
||||
auto sat_sector_writer = binary_writer<sector_id>(sat_sector);
|
||||
|
||||
read_sector(sat_sector_id, sat_sector_writer);
|
||||
|
||||
std::copy(sat_sector.begin(),
|
||||
sat_sector.end(),
|
||||
std::back_inserter(sat_));
|
||||
}
|
||||
|
||||
for (auto ssat_sector_id : follow_chain(header_.ssat_start, sat_))
|
||||
{
|
||||
auto ssat_sector = std::vector<sector_id>();
|
||||
auto ssat_sector_writer = binary_writer<sector_id>(ssat_sector);
|
||||
|
||||
read_sector(ssat_sector_id, ssat_sector_writer);
|
||||
|
||||
std::copy(ssat_sector.begin(),
|
||||
ssat_sector.end(),
|
||||
std::back_inserter(ssat_));
|
||||
}
|
||||
|
||||
tree_initialize_parent_maps();
|
||||
}
|
||||
|
||||
|
@ -113,32 +155,32 @@ compound_document::~compound_document()
|
|||
|
||||
std::size_t compound_document::sector_size()
|
||||
{
|
||||
return static_cast<std::size_t>(1) << header().sector_size_power;
|
||||
return static_cast<std::size_t>(1) << header_.sector_size_power;
|
||||
}
|
||||
|
||||
std::size_t compound_document::short_sector_size()
|
||||
{
|
||||
return static_cast<std::size_t>(1) << header().short_sector_size_power;
|
||||
return static_cast<std::size_t>(1) << header_.short_sector_size_power;
|
||||
}
|
||||
|
||||
std::vector<byte> compound_document::read_stream(const std::string &name)
|
||||
{
|
||||
const auto entry_id = find_entry(utf8_to_utf16(name));
|
||||
const auto entry_id = find_entry(name, compound_document_entry::entry_type::UserStream);
|
||||
const auto entry = entry_cache_.at(entry_id);
|
||||
|
||||
auto stream_data = std::vector<byte>();
|
||||
auto stream_data_writer = binary_writer<byte>(stream_data);
|
||||
|
||||
if (entry->size < header().threshold)
|
||||
if (entry->size < header_.threshold)
|
||||
{
|
||||
for (auto sector : follow_ssat_chain(entry->start))
|
||||
for (auto sector : follow_chain(entry->start, ssat_))
|
||||
{
|
||||
read_short_sector<byte>(sector, stream_data_writer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto sector : follow_sat_chain(entry->start))
|
||||
for (auto sector : follow_chain(entry->start, sat_))
|
||||
{
|
||||
read_sector<byte>(sector, stream_data_writer);
|
||||
}
|
||||
|
@ -150,15 +192,15 @@ std::vector<byte> compound_document::read_stream(const std::string &name)
|
|||
|
||||
void compound_document::write_stream(const std::string &name, const std::vector<std::uint8_t> &data)
|
||||
{
|
||||
auto entry_id = contains_entry(utf8_to_utf16(name))
|
||||
? find_entry(utf8_to_utf16(name))
|
||||
: insert_entry(utf8_to_utf16(name), compound_document_entry::entry_type::UserStream);
|
||||
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 = entry_cache_.at(entry_id);
|
||||
entry->size = static_cast<std::uint32_t>(data.size());
|
||||
|
||||
auto stream_data_reader = binary_reader<byte>(data);
|
||||
|
||||
if (entry->size < header().threshold)
|
||||
if (entry->size < header_.threshold)
|
||||
{
|
||||
const auto num_sectors = data.size() / short_sector_size()
|
||||
+ (data.size() % short_sector_size() ? 1 : 0);
|
||||
|
@ -166,7 +208,7 @@ void compound_document::write_stream(const std::string &name, const std::vector<
|
|||
auto chain = allocate_short_sectors(num_sectors);
|
||||
entry->start = chain.front();
|
||||
|
||||
for (auto sector : follow_ssat_chain(entry->start))
|
||||
for (auto sector : follow_chain(entry->start, ssat_))
|
||||
{
|
||||
write_short_sector(stream_data_reader, sector);
|
||||
}
|
||||
|
@ -179,7 +221,7 @@ void compound_document::write_stream(const std::string &name, const std::vector<
|
|||
auto chain = allocate_sectors(num_sectors);
|
||||
entry->start = chain.front();
|
||||
|
||||
for (auto sector : follow_sat_chain(entry->start))
|
||||
for (auto sector : follow_chain(entry->start, sat_))
|
||||
{
|
||||
write_sector(stream_data_reader, sector);
|
||||
}
|
||||
|
@ -196,182 +238,195 @@ void compound_document::write_sector(binary_reader<T> &reader, sector_id id)
|
|||
template<typename T>
|
||||
void compound_document::write_short_sector(binary_reader<T> &reader, sector_id id)
|
||||
{
|
||||
const auto header_size = sizeof(compound_document_header);
|
||||
auto first_short_sector = entry_cache_.at(find_entry(u"Root Entry"))->start;
|
||||
|
||||
auto short_position = static_cast<std::size_t>(short_sector_size() * id);
|
||||
auto current_sector_index = 0;
|
||||
auto current_sector = first_short_sector;
|
||||
|
||||
while (current_sector_index < short_position / sector_size())
|
||||
{
|
||||
current_sector = sat(current_sector);
|
||||
++current_sector_index;
|
||||
}
|
||||
|
||||
auto offset = short_position % sector_size();
|
||||
writer_->offset(header_size + sector_size() * current_sector + offset);
|
||||
writer_->append(reader, short_sector_size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void compound_document::read_sector(sector_id current, binary_writer<T> &writer)
|
||||
void compound_document::read_sector(sector_id id, binary_writer<T> &writer)
|
||||
{
|
||||
reader_->offset(sector_data_start() + sector_size() * current);
|
||||
writer.append<byte>(*reader_, sector_size());
|
||||
reader_->offset(sector_data_start() + sector_size() * id);
|
||||
writer.append(*reader_, sector_size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void compound_document::read_short_sector(sector_id current_short, binary_writer<T> &writer)
|
||||
void compound_document::read_short_sector(sector_id id, binary_writer<T> &writer)
|
||||
{
|
||||
const auto header_size = sizeof(compound_document_header);
|
||||
auto sector_data = std::vector<byte>();
|
||||
auto sector_data_writer = binary_writer<byte>(sector_data);
|
||||
auto first_short_sector = entry_cache_.at(find_entry(u"Root Entry"))->start;
|
||||
|
||||
auto short_position = static_cast<std::size_t>(short_sector_size() * current_short);
|
||||
auto current_sector_index = 0;
|
||||
auto current_sector = first_short_sector;
|
||||
|
||||
while (current_sector_index < short_position / sector_size())
|
||||
const auto container_chain = follow_chain(entry_cache_[0]->start, sat_);
|
||||
auto container = std::vector<byte>();
|
||||
auto container_writer = binary_writer<byte>(container);
|
||||
|
||||
for (auto sector : container_chain)
|
||||
{
|
||||
current_sector = sat(current_sector);
|
||||
++current_sector_index;
|
||||
read_sector(sector, container_writer);
|
||||
}
|
||||
|
||||
auto offset = short_position % sector_size();
|
||||
reader_->offset(header_size + sector_size() * current_sector + offset);
|
||||
sector_data_writer.append(*reader_, short_sector_size());
|
||||
auto container_reader = binary_reader<byte>(container);
|
||||
container_reader.offset(id * short_sector_size());
|
||||
|
||||
writer.append(container_reader, short_sector_size());
|
||||
}
|
||||
|
||||
sector_id compound_document::allocate_sector()
|
||||
{
|
||||
auto msat_index = std::size_t(0);
|
||||
auto sat_index = sector_id(0);
|
||||
|
||||
while (true)
|
||||
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())
|
||||
{
|
||||
if (header().msat[msat_index] == FreeSector)
|
||||
{
|
||||
// allocate msat sector
|
||||
header().msat[msat_index] = sat_index;
|
||||
|
||||
auto sector_data = std::vector<sector_id>(sector_size() / sizeof(sector_id), FreeSector);
|
||||
sector_data.front() = SATSector;
|
||||
auto sector_data_reader = binary_reader<sector_id>(sector_data);
|
||||
write_sector(sector_data_reader, sat_index);
|
||||
}
|
||||
|
||||
// load the current sector
|
||||
// could read one at a time, but this is easier for debugging for now
|
||||
sat_index = header().msat[msat_index];
|
||||
auto sat_sector_data = std::vector<sector_id>();
|
||||
auto sat_sector_data_writer = binary_writer<sector_id>(sat_sector_data);
|
||||
read_sector<sector_id>(sat_index, sat_sector_data_writer);
|
||||
|
||||
auto next_free_iter = std::find(sat_sector_data.begin(), sat_sector_data.end(), FreeSector);
|
||||
|
||||
if (next_free_iter != sat_sector_data.end()) // found a free sector
|
||||
{
|
||||
auto next_free = static_cast<sector_id>(next_free_iter - sat_sector_data.begin());
|
||||
|
||||
// write empty sector
|
||||
auto sector_data = std::vector<sector_id>(sector_size() / sizeof(sector_id), 0);
|
||||
auto sector_data_reader = binary_reader<sector_id>(sector_data);
|
||||
write_sector(sector_data_reader, next_free);
|
||||
|
||||
// update sat
|
||||
writer_->offset(sector_data_start() + sat_index * sector_size() + next_free * sizeof(sector_id));
|
||||
writer_->write(EndOfChain);
|
||||
|
||||
return next_free;
|
||||
}
|
||||
|
||||
++msat_index;
|
||||
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<sector_id>(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 = next_free_iter - sat_.begin();
|
||||
sat_[next_free] = EndOfChain;
|
||||
|
||||
auto next_free_msat_index = next_free / sectors_per_sector;;
|
||||
auto sat_index = msat_[next_free_msat_index];
|
||||
writer_->offset(sector_data_start()
|
||||
+ (sat_index * sector_size())
|
||||
+ (next_free % sectors_per_sector) * sizeof(sector_id));
|
||||
writer_->write(EndOfChain);
|
||||
|
||||
auto empty_sector = std::vector<byte>(sector_size());
|
||||
auto empty_sector_reader = binary_reader<byte>(empty_sector);
|
||||
write_sector(empty_sector_reader, next_free);
|
||||
|
||||
return next_free;
|
||||
}
|
||||
|
||||
sector_chain compound_document::allocate_sectors(std::size_t count)
|
||||
{
|
||||
auto chain = sector_chain();
|
||||
if (count == std::size_t(0)) return {};
|
||||
|
||||
for (auto i = std::size_t(0); i < count; ++i)
|
||||
auto chain = sector_chain();
|
||||
auto current = allocate_sector();
|
||||
|
||||
for (auto i = std::size_t(1); i < count; ++i)
|
||||
{
|
||||
chain.push_back(allocate_sector());
|
||||
chain.push_back(current);
|
||||
auto next = allocate_sector();
|
||||
sat_[current] = next;
|
||||
current = next;
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
sector_chain compound_document::follow_sat_chain(sector_id start)
|
||||
sector_chain compound_document::follow_chain(sector_id start, const sector_chain &table)
|
||||
{
|
||||
auto chain = sector_chain();
|
||||
const auto sectors_per_sector = sector_size() / sizeof(sector_id);
|
||||
const auto msat_index = start / sectors_per_sector;
|
||||
auto sector = start;
|
||||
auto current = start;
|
||||
|
||||
while (sector >= 0)
|
||||
while (current >= 0)
|
||||
{
|
||||
chain.push_back(sector);
|
||||
auto sat_index = header().msat[msat_index];
|
||||
reader_->offset(sector_data_start() + sat_index * sector_size() + (sector * sizeof(sector_id) % sector_size()));
|
||||
sector = reader_->read<sector_id>();
|
||||
chain.push_back(current);
|
||||
current = table[current];
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
sector_chain compound_document::follow_ssat_chain(sector_id start)
|
||||
{
|
||||
auto chain = sector_chain();
|
||||
return chain;
|
||||
}
|
||||
|
||||
sector_id compound_document::msat(sector_id id)
|
||||
{
|
||||
if (id < sector_id(109))
|
||||
{
|
||||
return header().msat.at(id);
|
||||
}
|
||||
|
||||
auto current = msat(108);
|
||||
|
||||
while (current < id)
|
||||
{
|
||||
reader_->offset(sector_data_start() + current * sector_size() - sizeof(sector_id));
|
||||
current = reader_->read<sector_id>();
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
sector_id compound_document::sat(sector_id id)
|
||||
{
|
||||
auto sectors_per_sector = sector_size() / sizeof(sector_id);
|
||||
auto msat_sector_index = msat(sector_id(id / sectors_per_sector));
|
||||
|
||||
reader_->offset(sector_data_start() + msat_sector_index * sector_size() + id % sectors_per_sector);
|
||||
|
||||
return reader_->read<sector_id>();
|
||||
}
|
||||
|
||||
sector_chain compound_document::allocate_short_sectors(std::size_t count)
|
||||
{
|
||||
return { EndOfChain };
|
||||
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;
|
||||
}
|
||||
|
||||
writer_->offset(0);
|
||||
writer_->write(header_);
|
||||
|
||||
auto old_size = ssat_.size();
|
||||
ssat_.resize(old_size + sectors_per_sector, FreeSector);
|
||||
|
||||
auto ssat_reader = binary_reader<sector_id>(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 = next_free_iter - ssat_.begin();
|
||||
ssat_[next_free] = EndOfChain;
|
||||
|
||||
auto sat_chain = follow_chain(header_.ssat_start, sat_);
|
||||
auto next_free_sat_chain_index = next_free / sectors_per_sector;
|
||||
auto sat_index = sat_chain[next_free_sat_chain_index];
|
||||
writer_->offset(sector_data_start()
|
||||
+ (sat_index * sectors_per_sector) * sizeof(sector_id)
|
||||
+ (next_free % sectors_per_sector) * sizeof(sector_id));
|
||||
writer_->write(EndOfChain);
|
||||
|
||||
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 (entry_cache_[0]->start < 0)
|
||||
{
|
||||
entry_cache_[0]->start = allocate_sector();
|
||||
}
|
||||
|
||||
auto container_chain = follow_chain(entry_cache_[0]->start, sat_);
|
||||
|
||||
if (required_container_sectors > container_chain.size())
|
||||
{
|
||||
sat_[container_chain.back()] = allocate_sector();
|
||||
}
|
||||
}
|
||||
|
||||
return next_free;
|
||||
}
|
||||
|
||||
directory_id compound_document::next_empty_entry()
|
||||
{
|
||||
auto entry_id = directory_id(0);
|
||||
|
||||
for (auto entry : entry_cache_)
|
||||
for (; entry_id < directory_id(entry_cache_.size()); ++entry_id)
|
||||
{
|
||||
if (entry.second->type == compound_document_entry::entry_type::Empty)
|
||||
{
|
||||
return entry.first;
|
||||
}
|
||||
auto entry = entry_cache_[entry_id];
|
||||
|
||||
entry_id = std::max(entry.first, entry_id);
|
||||
if (entry->type == compound_document_entry::entry_type::Empty)
|
||||
{
|
||||
return entry_id;
|
||||
}
|
||||
}
|
||||
|
||||
const auto entries_per_sector = sector_size()
|
||||
|
@ -396,7 +451,7 @@ directory_id compound_document::next_empty_entry()
|
|||
}
|
||||
|
||||
directory_id compound_document::insert_entry(
|
||||
const std::u16string &name,
|
||||
const std::string &name,
|
||||
compound_document_entry::entry_type type)
|
||||
{
|
||||
auto entry_id = next_empty_entry();
|
||||
|
@ -416,37 +471,22 @@ std::size_t compound_document::sector_data_start()
|
|||
return sizeof(compound_document_header);
|
||||
}
|
||||
|
||||
bool compound_document::contains_entry(const std::u16string &path)
|
||||
bool compound_document::contains_entry(const std::string &path,
|
||||
compound_document_entry::entry_type type)
|
||||
{
|
||||
return find_entry(path) >= 0;
|
||||
return find_entry(path, type) >= 0;
|
||||
}
|
||||
|
||||
compound_document_header &compound_document::header()
|
||||
directory_id compound_document::find_entry(const std::string &name,
|
||||
compound_document_entry::entry_type type)
|
||||
{
|
||||
reader_->offset(0);
|
||||
if (type == compound_document_entry::entry_type::RootStorage
|
||||
&& (name == "/" || name == "/Root Entry")) return 0;
|
||||
|
||||
if (reader_->bytes() < sizeof(compound_document_header))
|
||||
{
|
||||
if (writer_ != nullptr)
|
||||
{
|
||||
writer_->resize(sizeof(compound_document_header), 0);
|
||||
writer_->write(compound_document_header());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw xlnt::exception("missing header");
|
||||
}
|
||||
}
|
||||
|
||||
return const_cast<compound_document_header &>(
|
||||
reader_->read_reference<compound_document_header>());
|
||||
}
|
||||
|
||||
directory_id compound_document::find_entry(const std::u16string &name)
|
||||
{
|
||||
for (auto entry : entry_cache_)
|
||||
{
|
||||
if (entry.second->type == compound_document_entry::entry_type::Empty) continue;
|
||||
if (entry.second->type != type) continue;
|
||||
if (tree_path(entry.first) == name) return entry.first;
|
||||
}
|
||||
|
||||
|
@ -458,17 +498,16 @@ void compound_document::print_directory()
|
|||
for (auto entry : entry_cache_)
|
||||
{
|
||||
if (entry.second->type != compound_document_entry::entry_type::UserStream) continue;
|
||||
std::cout << utf16_to_utf8(tree_path(entry.first)) << std::endl;
|
||||
std::cout << tree_path(entry.first) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void compound_document::tree_initialize_parent_maps()
|
||||
{
|
||||
const auto entries_per_sector = static_cast<directory_id>(sector_size()
|
||||
/ sizeof(compound_document_entry));
|
||||
const auto entries_per_sector = sector_size() / sizeof(compound_document_entry);
|
||||
auto entry_id = directory_id(0);
|
||||
|
||||
for (auto sector : follow_sat_chain(header().directory_start))
|
||||
for (auto sector : follow_chain(header_.directory_start, sat_))
|
||||
{
|
||||
reader_->offset(sector_data_start() + sector * sector_size());
|
||||
|
||||
|
@ -557,7 +596,7 @@ void compound_document::tree_insert(directory_id new_id, directory_id storage_id
|
|||
{
|
||||
y = x;
|
||||
|
||||
if (compare_keys(tree_key(x), tree_key(new_id)) > 0)
|
||||
if (compare_keys(tree_key(new_id), tree_key(x)) > 0)
|
||||
{
|
||||
x = tree_right(x);
|
||||
}
|
||||
|
@ -569,7 +608,7 @@ void compound_document::tree_insert(directory_id new_id, directory_id storage_id
|
|||
|
||||
tree_parent(new_id) = y;
|
||||
|
||||
if (compare_keys(tree_key(y), tree_key(new_id)) > 0)
|
||||
if (compare_keys(tree_key(new_id), tree_key(y)) > 0)
|
||||
{
|
||||
tree_right(y) = new_id;
|
||||
}
|
||||
|
@ -581,18 +620,18 @@ void compound_document::tree_insert(directory_id new_id, directory_id storage_id
|
|||
tree_insert_fixup(new_id);
|
||||
}
|
||||
|
||||
std::u16string compound_document::tree_path(directory_id id)
|
||||
std::string compound_document::tree_path(directory_id id)
|
||||
{
|
||||
auto storage_id = parent_storage_[id];
|
||||
auto result = std::u16string();
|
||||
auto result = std::vector<std::string>();
|
||||
|
||||
while (storage_id > 0)
|
||||
{
|
||||
storage_id = parent_storage_[storage_id];
|
||||
result = tree_key(storage_id) + u"/" + result;
|
||||
result.push_back(entry_cache_[storage_id]->name());
|
||||
}
|
||||
|
||||
return result;
|
||||
return "/" + join_path(result) + entry_cache_[id]->name();
|
||||
}
|
||||
|
||||
void compound_document::tree_rotate_left(directory_id x)
|
||||
|
@ -673,7 +712,7 @@ void compound_document::tree_insert_fixup(directory_id x)
|
|||
{
|
||||
auto y = tree_right(tree_parent(tree_parent(x)));
|
||||
|
||||
if (tree_color(y) == entry_color::Red)
|
||||
if (y >= 0 && tree_color(y) == entry_color::Red)
|
||||
{
|
||||
// case 1
|
||||
tree_color(tree_parent(x)) = entry_color::Black;
|
||||
|
@ -700,7 +739,7 @@ void compound_document::tree_insert_fixup(directory_id x)
|
|||
{
|
||||
auto y = tree_left(tree_parent(tree_parent(x)));
|
||||
|
||||
if (tree_color(y) == entry_color::Red)
|
||||
if (y >= 0 && tree_color(y) == entry_color::Red)
|
||||
{
|
||||
//case 1
|
||||
tree_color(tree_parent(x)) = entry_color::Black;
|
||||
|
@ -753,7 +792,7 @@ directory_id &compound_document::tree_child(directory_id id)
|
|||
return entry_cache_[id]->child;
|
||||
}
|
||||
|
||||
std::u16string compound_document::tree_key(directory_id id)
|
||||
std::string compound_document::tree_key(directory_id id)
|
||||
{
|
||||
return entry_cache_[id]->name();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include <detail/binary.hpp>
|
||||
#include <detail/unicode.hpp>
|
||||
|
||||
namespace xlnt {
|
||||
namespace detail {
|
||||
|
@ -63,23 +64,24 @@ struct compound_document_header
|
|||
std::uint32_t num_short_sectors = 0;
|
||||
sector_id extra_msat_start = -2;
|
||||
std::uint32_t num_extra_msat_sectors = 0;
|
||||
std::array<sector_id, 109> msat = { 0 };
|
||||
std::array<sector_id, 109> msat = {{0}};
|
||||
};
|
||||
|
||||
struct compound_document_entry
|
||||
{
|
||||
void name(const std::u16string &new_name)
|
||||
void name(const std::string &new_name)
|
||||
{
|
||||
name_length = std::min(static_cast<std::uint16_t>(new_name.size()), std::uint16_t(31));
|
||||
std::copy(new_name.begin(), new_name.begin() + name_length, name_array.begin());
|
||||
auto u16_name = utf8_to_utf16(new_name);
|
||||
name_length = std::min(static_cast<std::uint16_t>(u16_name.size()), std::uint16_t(31));
|
||||
std::copy(u16_name.begin(), u16_name.begin() + name_length, name_array.begin());
|
||||
name_array[name_length] = 0;
|
||||
name_length = (name_length + 1) * 2;
|
||||
}
|
||||
|
||||
std::u16string name() const
|
||||
std::string name() const
|
||||
{
|
||||
return std::u16string(name_array.begin(),
|
||||
name_array.begin() + (name_length - 1) / 2);
|
||||
return utf16_to_utf8(std::u16string(name_array.begin(),
|
||||
name_array.begin() + (name_length - 1) / 2));
|
||||
}
|
||||
|
||||
enum class entry_type : std::uint8_t
|
||||
|
@ -136,11 +138,7 @@ private:
|
|||
std::size_t short_sector_size();
|
||||
std::size_t sector_data_start();
|
||||
|
||||
sector_chain follow_sat_chain(sector_id start);
|
||||
sector_chain follow_ssat_chain(sector_id start);
|
||||
|
||||
sector_id msat(sector_id id);
|
||||
sector_id sat(sector_id id);
|
||||
sector_chain follow_chain(sector_id start, const sector_chain &table);
|
||||
|
||||
void print_directory();
|
||||
|
||||
|
@ -153,19 +151,19 @@ private:
|
|||
sector_id allocate_short_sector();
|
||||
sector_chain allocate_short_sectors(std::size_t sectors);
|
||||
|
||||
compound_document_header &header();
|
||||
|
||||
bool contains_entry(const std::u16string &path);
|
||||
directory_id find_entry(const std::u16string &path);
|
||||
bool contains_entry(const std::string &path,
|
||||
compound_document_entry::entry_type type);
|
||||
directory_id find_entry(const std::string &path,
|
||||
compound_document_entry::entry_type type);
|
||||
directory_id next_empty_entry();
|
||||
directory_id insert_entry(const std::u16string &path,
|
||||
directory_id insert_entry(const std::string &path,
|
||||
compound_document_entry::entry_type type);
|
||||
|
||||
// Red black tree helper functions
|
||||
void tree_initialize_parent_maps();
|
||||
void tree_insert(directory_id new_id, directory_id storage_id);
|
||||
void tree_insert_fixup(directory_id x);
|
||||
std::u16string tree_path(directory_id id);
|
||||
std::string tree_path(directory_id id);
|
||||
void tree_rotate_left(directory_id x);
|
||||
void tree_rotate_right(directory_id y);
|
||||
directory_id &tree_left(directory_id id);
|
||||
|
@ -173,11 +171,16 @@ private:
|
|||
directory_id &tree_parent(directory_id id);
|
||||
directory_id &tree_root(directory_id id);
|
||||
directory_id &tree_child(directory_id id);
|
||||
std::u16string tree_key(directory_id id);
|
||||
std::string tree_key(directory_id id);
|
||||
compound_document_entry::entry_color &tree_color(directory_id id);
|
||||
|
||||
std::unique_ptr<binary_reader<byte>> reader_;
|
||||
std::unique_ptr<binary_writer<byte>> writer_;
|
||||
std::unique_ptr<binary_reader<byte>> reader_;
|
||||
|
||||
compound_document_header header_;
|
||||
sector_chain msat_;
|
||||
sector_chain sat_;
|
||||
sector_chain ssat_;
|
||||
|
||||
std::unordered_map<directory_id, directory_id> parent_storage_;
|
||||
std::unordered_map<directory_id, directory_id> parent_;
|
||||
|
|
|
@ -73,15 +73,18 @@ void print_summary()
|
|||
|
||||
int main()
|
||||
{
|
||||
const auto bytes2 = xlnt::detail::to_vector(std::ifstream("C:/Users/Thomas/Development/xlnt/tests/data/6_encrypted_libre.xlsx", std::ios::binary));
|
||||
std::ifstream file("/Users/thomas/Development/xlnt/tests/data/6_encrypted_libre.xlsx", std::ios::binary);
|
||||
const auto bytes2 = xlnt::detail::to_vector(file);
|
||||
xlnt::detail::compound_document doc2(bytes2);
|
||||
auto info = doc2.read_stream("/EncryptionInfo");
|
||||
|
||||
std::vector<std::uint8_t> bytes;
|
||||
xlnt::detail::compound_document doc(bytes);
|
||||
doc.write_stream("aaa", std::vector<std::uint8_t>(4095, 'a'));
|
||||
doc.write_stream("bbb", std::vector<std::uint8_t>(4095, 'b'));
|
||||
doc.write_stream("ccc", std::vector<std::uint8_t>(4095, 'c'));
|
||||
xlnt::detail::to_stream(bytes, std::ofstream("cd.xlsx", std::ios::binary));
|
||||
std::ofstream file2("cd.xlsx", std::ios::binary);
|
||||
xlnt::detail::to_stream(bytes, file2);
|
||||
|
||||
// cell
|
||||
run_tests<cell_test_suite>();
|
||||
|
|
Loading…
Reference in New Issue
Block a user