• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xlnt-community / xlnt / b5325487-b953-4fb3-ae7c-12335ef12dcb

02 Feb 2025 07:13PM UTC coverage: 81.7% (-1.8%) from 83.482%
b5325487-b953-4fb3-ae7c-12335ef12dcb

Pull #55

circleci

doomlaur
Fix CI to use the correct Docker image for code coverage tests
Pull Request #55: Add support for C++20 and C++23, and experimental support for C++26

78 of 142 new or added lines in 8 files covered. (54.93%)

304 existing lines in 28 files now uncovered.

11460 of 14027 relevant lines covered (81.7%)

1191795.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.56
./source/detail/cryptography/compound_document.cpp
1
// Copyright (C) 2016-2022 Thomas Fussell
2
// Copyright (C) 2002-2007 Ariya Hidayat (ariya@kde.org).
3
// Copyright (c) 2024-2025 xlnt-community
4
//
5
// Redistribution and use in source and binary forms, with or without
6
// modification, are permitted provided that the following conditions
7
// are met:
8
//
9
// 1. Redistributions of source code must retain the above copyright
10
// notice, this list of conditions and the following disclaimer.
11
// 2. Redistributions in binary form must reproduce the above copyright
12
// notice, this list of conditions and the following disclaimer in the
13
// documentation and/or other materials provided with the distribution.
14
//
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25

26
#include <algorithm>
27
#include <array>
28
#include <cstring>
29
#include <iostream>
30
#include <locale>
31
#include <string>
32
#include <vector>
33

34
#include <xlnt/utils/exceptions.hpp>
35
#include <detail/binary.hpp>
36
#include <detail/cryptography/compound_document.hpp>
37
#include <detail/unicode.hpp>
38

39
namespace {
40

41
using namespace xlnt::detail;
42

43
int compare_keys(const std::string &left, const std::string &right)
8✔
44
{
45
    auto to_lower = [](std::string s) {
16✔
46
        static const auto locale = std::locale();
16✔
47
        std::use_facet<std::ctype<char>>(locale).tolower(&s[0], &s[0] + s.size());
16✔
48

49
        return s;
16✔
50
    };
51

52
    return to_lower(left).compare(to_lower(right));
8✔
53
}
54

55
std::vector<std::string> split_path(const std::string &path)
12✔
56
{
57
    auto split = std::vector<std::string>();
12✔
58
    auto current = path.find('/');
12✔
59
    auto prev = std::size_t(0);
12✔
60

61
    while (current != std::string::npos)
24✔
62
    {
63
        split.push_back(path.substr(prev, current - prev));
12✔
64
        prev = current + 1;
12✔
65
        current = path.find('/', prev);
12✔
66
    }
67

68
    split.push_back(path.substr(prev));
12✔
69

70
    return split;
12✔
UNCOV
71
}
×
72

73
std::string join_path(const std::vector<std::string> &path)
138✔
74
{
75
    auto joined = std::string();
138✔
76

77
    for (auto part : path)
236✔
78
    {
79
        joined.append(part);
98✔
80
        joined.push_back('/');
98✔
81
    }
98✔
82

83
    return joined;
138✔
UNCOV
84
}
×
85

86
const sector_id FreeSector = -1;
87
const sector_id EndOfChain = -2;
88
const sector_id SATSector = -3;
89
//const sector_id MSATSector = -4;
90

91
const directory_id End = -1;
92

93
} // namespace
94

95
namespace xlnt {
96
namespace detail {
97

98
/// <summary>
99
/// Allows a std::vector to be read through a std::istream.
100
/// </summary>
101
class compound_document_istreambuf : public std::streambuf
102
{
103
    using int_type = std::streambuf::int_type;
104

105
public:
106
    compound_document_istreambuf(const compound_document_entry &entry, compound_document &document)
26✔
107
        : entry_(entry),
52✔
108
          document_(document),
26✔
109
          sector_writer_(current_sector_),
26✔
110
          position_(0)
26✔
111
    {
112
    }
26✔
113

114
    compound_document_istreambuf(const compound_document_istreambuf &) = delete;
115
    compound_document_istreambuf &operator=(const compound_document_istreambuf &) = delete;
116

117
    ~compound_document_istreambuf() override;
118

119
private:
120
    std::streamsize xsgetn(char *c, std::streamsize count) override
406✔
121
    {
122
        auto bytes_read = std::streamsize(0);
406✔
123

124
        const auto sector_chain = short_stream() ? document_.ssat_ : document_.sat_;
406✔
125
        const auto chain = document_.follow_chain(entry_.start, sector_chain);
406✔
126
        const auto sector_size = short_stream() ? document_.short_sector_size() : document_.sector_size();
406✔
127
        auto current_sector = chain[position_ / sector_size];
406✔
128
        auto remaining = std::min(std::size_t(entry_.size) - position_, std::size_t(count));
406✔
129

130
        while (remaining)
2,967✔
131
        {
132
            if (current_sector_.empty() || chain[position_ / sector_size] != current_sector)
2,561✔
133
            {
134
                current_sector = chain[position_ / sector_size];
2,180✔
135
                sector_writer_.reset();
2,180✔
136
                if (short_stream())
2,180✔
137
                {
138
                    document_.read_short_sector(current_sector, sector_writer_);
153✔
139
                }
140
                else
141
                {
142
                    document_.read_sector(current_sector, sector_writer_);
2,027✔
143
                }
144
            }
145

146
            const auto available = std::min(entry_.size - position_,
5,122✔
147
                sector_size - position_ % sector_size);
2,561✔
148
            const auto to_read = std::min(available, std::size_t(remaining));
2,561✔
149

150
            auto start = current_sector_.begin() + static_cast<std::ptrdiff_t>(position_ % sector_size);
2,561✔
151
            auto end = start + static_cast<std::ptrdiff_t>(to_read);
2,561✔
152

153
            for (auto i = start; i < end; ++i)
1,046,896✔
154
            {
155
                *(c++) = static_cast<char>(*i);
1,044,335✔
156
            }
157

158
            remaining -= to_read;
2,561✔
159
            position_ += to_read;
2,561✔
160
            bytes_read += to_read;
2,561✔
161
        }
162

163
        if (position_ < entry_.size && chain[position_ / sector_size] != current_sector)
406✔
164
        {
165
            current_sector = chain[position_ / sector_size];
6✔
166
            sector_writer_.reset();
6✔
167
            if (short_stream())
6✔
168
            {
169
                document_.read_short_sector(current_sector, sector_writer_);
6✔
170
            }
171
            else
172
            {
173
                document_.read_sector(current_sector, sector_writer_);
×
174
            }
175
        }
176

177
        return bytes_read;
406✔
178
    }
406✔
179

180
    bool short_stream()
2,998✔
181
    {
182
        return entry_.size < document_.header_.threshold;
2,998✔
183
    }
184

185
    int_type underflow() override
×
186
    {
187
        if (position_ >= entry_.size)
×
188
        {
189
            return traits_type::eof();
×
190
        }
191

192
        auto old_position = position_;
×
193
        auto result = '\0';
×
194
        xsgetn(&result, 1);
×
195
        position_ = old_position;
×
196

197
        return result;
×
198
    }
199

200
    int_type uflow() override
×
201
    {
202
        auto result = underflow();
×
203
        ++position_;
×
204

205
        return result;
×
206
    }
207

208
    std::streamsize showmanyc() override
×
209
    {
210
        if (position_ == entry_.size)
×
211
        {
212
            return static_cast<std::streamsize>(-1);
×
213
        }
214

215
        return static_cast<std::streamsize>(entry_.size - position_);
×
216
    }
217

218
    std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode) override
12✔
219
    {
220
        if (way == std::ios_base::beg)
12✔
221
        {
222
            position_ = 0;
×
223
        }
224
        else if (way == std::ios_base::end)
12✔
225
        {
226
            position_ = entry_.size;
×
227
        }
228

229
        if (off < 0)
12✔
230
        {
231
            if (static_cast<std::size_t>(-off) > position_)
×
232
            {
233
                position_ = 0;
×
234
                return static_cast<std::ptrdiff_t>(-1);
×
235
            }
236
            else
237
            {
238
                position_ -= static_cast<std::size_t>(-off);
×
239
            }
240
        }
241
        else if (off > 0)
12✔
242
        {
243
            if (static_cast<std::size_t>(off) + position_ > entry_.size)
×
244
            {
245
                position_ = entry_.size;
×
246
                return static_cast<std::ptrdiff_t>(-1);
×
247
            }
248
            else
249
            {
250
                position_ += static_cast<std::size_t>(off);
×
251
            }
252
        }
253

254
        return static_cast<std::ptrdiff_t>(position_);
12✔
255
    }
256

257
    std::streampos seekpos(std::streampos sp, std::ios_base::openmode) override
×
258
    {
259
        if (sp < 0)
×
260
        {
261
            position_ = 0;
×
262
        }
263
        else if (static_cast<std::size_t>(sp) > entry_.size)
×
264
        {
265
            position_ = entry_.size;
×
266
        }
267
        else
268
        {
269
            position_ = static_cast<std::size_t>(sp);
×
270
        }
271

272
        return static_cast<std::ptrdiff_t>(position_);
×
273
    }
274

275
private:
276
    const compound_document_entry &entry_;
277
    compound_document &document_;
278
    std::vector<byte> current_sector_;
279
    binary_writer<byte> sector_writer_;
280
    std::size_t position_;
281
};
282

283
compound_document_istreambuf::~compound_document_istreambuf()
52✔
284
{
285
}
52✔
286

287
/// <summary>
288
/// Allows a std::vector to be written through a std::ostream.
289
/// </summary>
290
class compound_document_ostreambuf : public std::streambuf
291
{
292
    using int_type = std::streambuf::int_type;
293

294
public:
295
    compound_document_ostreambuf(compound_document_entry &entry, compound_document &document)
8✔
296
        : entry_(entry),
16✔
297
          document_(document),
8✔
298
          current_sector_(document.header_.threshold),
8✔
299
          sector_reader_(current_sector_),
8✔
300
          position_(0)
16✔
301
    {
302
        setp(reinterpret_cast<char *>(current_sector_.data()),
8✔
303
            reinterpret_cast<char *>(current_sector_.data() + current_sector_.size()));
8✔
304
    }
8✔
305

306
    compound_document_ostreambuf(const compound_document_ostreambuf &) = delete;
307
    compound_document_ostreambuf &operator=(const compound_document_ostreambuf &) = delete;
308

309
    ~compound_document_ostreambuf() override;
310

311
private:
312
    int sync() override
986✔
313
    {
314
        auto written = static_cast<std::size_t>(pptr() - pbase());
986✔
315

316
        if (written == std::size_t(0))
986✔
317
        {
318
            return 0;
4✔
319
        }
320

321
        sector_reader_.reset();
982✔
322

323
        if (short_stream())
982✔
324
        {
325
            if (position_ + written >= document_.header_.threshold)
8✔
326
            {
327
                convert_to_long_stream();
4✔
328
            }
329
            else
330
            {
331
                if (entry_.start < 0)
4✔
332
                {
333
                    auto num_sectors = (position_ + written + document_.short_sector_size() - 1) / document_.short_sector_size();
4✔
334
                    chain_ = document_.allocate_short_sectors(num_sectors);
4✔
335
                    entry_.start = chain_.front();
4✔
336
                }
337

338
                for (auto link : chain_)
80✔
339
                {
340
                    document_.write_short_sector(sector_reader_, link);
76✔
341
                    sector_reader_.offset(sector_reader_.offset() + document_.short_sector_size());
76✔
342
                }
343
            }
344
        }
345
        else
346
        {
347
            const auto sector_index = position_ / document_.sector_size();
974✔
348
            document_.write_sector(sector_reader_, chain_[sector_index]);
974✔
349
        }
350

351
        position_ += written;
982✔
352
        entry_.size = std::max(entry_.size, static_cast<std::uint32_t>(position_));
982✔
353
        document_.write_directory();
982✔
354

355
        std::fill(current_sector_.begin(), current_sector_.end(), byte(0));
982✔
356
        setp(reinterpret_cast<char *>(current_sector_.data()),
982✔
357
            reinterpret_cast<char *>(current_sector_.data() + current_sector_.size()));
982✔
358

359
        return 0;
982✔
360
    }
361

362
    bool short_stream()
1,956✔
363
    {
364
        return entry_.size < document_.header_.threshold;
1,956✔
365
    }
366

367
    int_type overflow(int_type c = traits_type::eof()) override
974✔
368
    {
369
        sync();
974✔
370

371
        if (short_stream())
974✔
372
        {
373
            auto next_sector = document_.allocate_short_sector();
×
374
            document_.ssat_[static_cast<std::size_t>(chain_.back())] = next_sector;
×
375
            chain_.push_back(next_sector);
×
376
            document_.write_ssat();
×
377
        }
378
        else
379
        {
380
            auto next_sector = document_.allocate_sector();
974✔
381
            document_.sat_[static_cast<std::size_t>(chain_.back())] = next_sector;
974✔
382
            chain_.push_back(next_sector);
974✔
383
            document_.write_sat();
974✔
384
        }
385

386
        auto value = static_cast<std::uint8_t>(c);
974✔
387

388
        if (c != traits_type::eof())
974✔
389
        {
390
            current_sector_[position_ % current_sector_.size()] = value;
974✔
391
        }
392

393
        pbump(1);
974✔
394

395
        return traits_type::to_int_type(static_cast<char>(value));
974✔
396
    }
397

398
    void convert_to_long_stream()
4✔
399
    {
400
        sector_reader_.reset();
4✔
401

402
        auto num_sectors = current_sector_.size() / document_.sector_size();
4✔
403
        auto new_chain = document_.allocate_sectors(num_sectors);
4✔
404

405
        for (auto link : new_chain)
36✔
406
        {
407
            document_.write_sector(sector_reader_, link);
32✔
408
            sector_reader_.offset(sector_reader_.offset() + document_.short_sector_size());
32✔
409
        }
410

411
        current_sector_.resize(document_.sector_size(), 0);
4✔
412
        std::fill(current_sector_.begin(), current_sector_.end(), byte(0));
4✔
413

414
        if (entry_.start < 0)
4✔
415
        {
416
            // TODO: deallocate short sectors here
417
            if (document_.header_.num_short_sectors == 0)
4✔
418
            {
419
                document_.entries_[0].start = EndOfChain;
×
420
            }
421
        }
422

423
        chain_ = new_chain;
4✔
424
        entry_.start = chain_.front();
4✔
425
        document_.write_directory();
4✔
426
    }
4✔
427

428
    std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode) override
×
429
    {
430
        if (way == std::ios_base::beg)
×
431
        {
432
            position_ = 0;
×
433
        }
434
        else if (way == std::ios_base::end)
×
435
        {
436
            position_ = entry_.size;
×
437
        }
438

439
        if (off < 0)
×
440
        {
441
            if (static_cast<std::size_t>(-off) > position_)
×
442
            {
443
                position_ = 0;
×
444
                return static_cast<std::ptrdiff_t>(-1);
×
445
            }
446
            else
447
            {
448
                position_ -= static_cast<std::size_t>(-off);
×
449
            }
450
        }
451
        else if (off > 0)
×
452
        {
453
            if (static_cast<std::size_t>(off) + position_ > entry_.size)
×
454
            {
455
                position_ = entry_.size;
×
456
                return static_cast<std::ptrdiff_t>(-1);
×
457
            }
458
            else
459
            {
460
                position_ += static_cast<std::size_t>(off);
×
461
            }
462
        }
463

464
        return static_cast<std::ptrdiff_t>(position_);
×
465
    }
466

467
    std::streampos seekpos(std::streampos sp, std::ios_base::openmode) override
×
468
    {
469
        if (sp < 0)
×
470
        {
471
            position_ = 0;
×
472
        }
473
        else if (static_cast<std::size_t>(sp) > entry_.size)
×
474
        {
475
            position_ = entry_.size;
×
476
        }
477
        else
478
        {
479
            position_ = static_cast<std::size_t>(sp);
×
480
        }
481

482
        return static_cast<std::ptrdiff_t>(position_);
×
483
    }
484

485
private:
486
    compound_document_entry &entry_;
487
    compound_document &document_;
488
    std::vector<byte> current_sector_;
489
    binary_reader<byte> sector_reader_;
490
    std::size_t position_;
491
    sector_chain chain_;
492
};
493

494
compound_document_ostreambuf::~compound_document_ostreambuf()
16✔
495
{
496
    sync();
8✔
497
}
16✔
498

499
compound_document::compound_document(std::ostream &out)
4✔
500
    : out_(&out),
4✔
501
      stream_in_(nullptr),
4✔
502
      stream_out_(nullptr)
8✔
503
{
504
    header_.msat.fill(FreeSector);
4✔
505
    write_header();
4✔
506
    insert_entry("/Root Entry", compound_document_entry::entry_type::RootStorage);
4✔
507
}
4✔
508

509
compound_document::compound_document(std::istream &in)
13✔
510
    : in_(&in),
13✔
511
      stream_in_(nullptr),
13✔
512
      stream_out_(nullptr)
26✔
513
{
514
    read_header();
13✔
515
    read_msat();
13✔
516
    read_sat();
13✔
517
    read_ssat();
13✔
518
    read_directory();
13✔
519
}
13✔
520

521
compound_document::~compound_document()
17✔
522
{
523
    close();
17✔
524
}
17✔
525

526
void compound_document::close()
17✔
527
{
528
    stream_out_buffer_.reset(nullptr);
17✔
529
}
17✔
530

531
std::size_t compound_document::sector_size()
41,121✔
532
{
533
    return static_cast<std::size_t>(1) << header_.sector_size_power;
41,121✔
534
}
535

536
std::size_t compound_document::short_sector_size()
950✔
537
{
538
    return static_cast<std::size_t>(1) << header_.short_sector_size_power;
950✔
539
}
540

541
std::istream &compound_document::open_read_stream(const std::string &name)
26✔
542
{
543
    if (!contains_entry(name, compound_document_entry::entry_type::UserStream))
26✔
544
    {
545
        throw xlnt::exception("not found");
×
546
    }
547

548
    const auto entry_id = find_entry(name, compound_document_entry::entry_type::UserStream);
26✔
549
    const auto &entry = entries_.at(static_cast<std::size_t>(entry_id));
26✔
550

551
    stream_in_buffer_.reset(new compound_document_istreambuf(entry, *this));
26✔
552
    stream_in_.rdbuf(stream_in_buffer_.get());
26✔
553

554
    return stream_in_;
26✔
555
}
556

557
std::ostream &compound_document::open_write_stream(const std::string &name)
8✔
558
{
559
    auto entry_id = contains_entry(name, compound_document_entry::entry_type::UserStream)
8✔
560
        ? find_entry(name, compound_document_entry::entry_type::UserStream)
8✔
561
        : insert_entry(name, compound_document_entry::entry_type::UserStream);
8✔
562
    auto &entry = entries_.at(static_cast<std::size_t>(entry_id));
8✔
563

564
    stream_out_buffer_.reset(new compound_document_ostreambuf(entry, *this));
8✔
565
    stream_out_.rdbuf(stream_out_buffer_.get());
8✔
566

567
    return stream_out_;
8✔
568
}
569

570
template <typename T>
571
void compound_document::write_sector(binary_reader<T> &reader, sector_id id)
10,579✔
572
{
573
    out_->seekp(static_cast<std::ptrdiff_t>(sector_data_start() + sector_size() * static_cast<std::size_t>(id)));
10,579✔
574
    out_->write(reinterpret_cast<const char *>(reader.data() + reader.offset()),
10,579✔
575
        static_cast<std::ptrdiff_t>(std::min(sector_size(), reader.bytes() - reader.offset())));
10,579✔
576
}
10,579✔
577

578
template <typename T>
579
void compound_document::write_short_sector(binary_reader<T> &reader, sector_id id)
76✔
580
{
581
    auto chain = follow_chain(entries_[0].start, sat_);
76✔
582
    auto sector_id = chain[static_cast<std::size_t>(id) / (sector_size() / short_sector_size())];
76✔
583
    auto sector_offset = static_cast<std::size_t>(id) % (sector_size() / short_sector_size()) * short_sector_size();
76✔
584
    out_->seekp(static_cast<std::ptrdiff_t>(sector_data_start() + sector_size() * static_cast<std::size_t>(sector_id) + sector_offset));
76✔
585
    out_->write(reinterpret_cast<const char *>(reader.data() + reader.offset()),
76✔
586
        static_cast<std::ptrdiff_t>(std::min(short_sector_size(), reader.bytes() - reader.offset())));
76✔
587
}
76✔
588

589
template <typename T>
590
void compound_document::read_sector(sector_id id, binary_writer<T> &writer)
2,658✔
591
{
592
    in_->seekg(static_cast<std::ptrdiff_t>(sector_data_start() + sector_size() * static_cast<std::size_t>(id)));
2,658✔
593
    std::vector<byte> sector(sector_size(), 0);
2,658✔
594
    in_->read(reinterpret_cast<char *>(sector.data()), static_cast<std::ptrdiff_t>(sector_size()));
2,658✔
595
    writer.append(sector);
2,658✔
596
}
2,658✔
597

598
template <typename T>
599
void compound_document::read_sector_chain(sector_id start, binary_writer<T> &writer)
600
{
601
    for (auto link : follow_chain(start, sat_))
602
    {
603
        read_sector(link, writer);
604
    }
605
}
606

607
template <typename T>
608
void compound_document::read_sector_chain(sector_id start, binary_writer<T> &writer, sector_id offset, std::size_t count)
609
{
610
    auto chain = follow_chain(start, sat_);
611

612
    for (auto i = std::size_t(0); i < count; ++i)
613
    {
614
        read_sector(chain[offset + i], writer);
615
    }
616
}
617

618
template <typename T>
619
void compound_document::read_short_sector(sector_id id, binary_writer<T> &writer)
159✔
620
{
621
    const auto container_chain = follow_chain(entries_[0].start, sat_);
159✔
622
    auto container = std::vector<byte>();
159✔
623
    auto container_writer = binary_writer<byte>(container);
159✔
624

625
    for (auto sector : container_chain)
735✔
626
    {
627
        read_sector(sector, container_writer);
576✔
628
    }
629

630
    auto container_reader = binary_reader<byte>(container);
159✔
631
    container_reader.offset(static_cast<std::size_t>(id) * short_sector_size());
159✔
632

633
    writer.append(container_reader, short_sector_size());
159✔
634
}
159✔
635

636
template <typename T>
637
void compound_document::read_short_sector_chain(sector_id start, binary_writer<T> &writer)
638
{
639
    for (auto link : follow_chain(start, ssat_))
640
    {
641
        read_short_sector(link, writer);
642
    }
643
}
644

645
template <typename T>
646
void compound_document::read_short_sector_chain(sector_id start, binary_writer<T> &writer, sector_id offset, std::size_t count)
647
{
648
    auto chain = follow_chain(start, ssat_);
649

650
    for (auto i = std::size_t(0); i < count; ++i)
651
    {
652
        read_short_sector(chain[offset + i], writer);
653
    }
654
}
655

656
sector_id compound_document::allocate_sector()
1,026✔
657
{
658
    const auto sectors_per_sector = sector_size() / sizeof(sector_id);
1,026✔
659
    auto next_free_iter = std::find(sat_.begin(), sat_.end(), FreeSector);
1,026✔
660

661
    if (next_free_iter == sat_.end())
1,026✔
662
    {
663
        auto next_msat_index = header_.num_msat_sectors;
11✔
664
        auto new_sat_sector_id = sector_id(sat_.size());
11✔
665

666
        msat_.push_back(new_sat_sector_id);
11✔
667
        write_msat();
11✔
668

669
        header_.msat[msat_.size() - 1] = new_sat_sector_id;
11✔
670
        ++header_.num_msat_sectors;
11✔
671
        write_header();
11✔
672

673
        sat_.resize(sat_.size() + sectors_per_sector, FreeSector);
11✔
674
        sat_[static_cast<std::size_t>(new_sat_sector_id)] = SATSector;
11✔
675

676
        auto sat_reader = binary_reader<sector_id>(sat_);
11✔
677
        sat_reader.offset(next_msat_index * sectors_per_sector);
11✔
678
        write_sector(sat_reader, new_sat_sector_id);
11✔
679

680
        next_free_iter = std::find(sat_.begin(), sat_.end(), FreeSector);
11✔
681
    }
11✔
682

683
    auto next_free = sector_id(next_free_iter - sat_.begin());
1,026✔
684
    sat_[static_cast<std::size_t>(next_free)] = EndOfChain;
1,026✔
685

686
    write_sat();
1,026✔
687

688
    auto empty_sector = std::vector<byte>(sector_size());
1,026✔
689
    auto empty_sector_reader = binary_reader<byte>(empty_sector);
1,026✔
690
    write_sector(empty_sector_reader, next_free);
1,026✔
691

692
    return next_free;
1,026✔
693
}
1,026✔
694

695
sector_chain compound_document::allocate_sectors(std::size_t count)
4✔
696
{
697
    if (count == std::size_t(0)) return {};
4✔
698

699
    auto chain = sector_chain();
4✔
700
    auto current = allocate_sector();
4✔
701

702
    for (auto i = std::size_t(1); i < count; ++i)
32✔
703
    {
704
        chain.push_back(current);
28✔
705
        auto next = allocate_sector();
28✔
706
        sat_[static_cast<std::size_t>(current)] = next;
28✔
707
        current = next;
28✔
708
    }
709

710
    chain.push_back(current);
4✔
711
    write_sat();
4✔
712

713
    return chain;
4✔
714
}
4✔
715

716
sector_chain compound_document::follow_chain(sector_id start, const sector_chain &table)
4,967✔
717
{
718
    auto chain = sector_chain();
4,967✔
719
    auto current = start;
4,967✔
720

721
    while (current >= 0)
246,261✔
722
    {
723
        chain.push_back(current);
241,294✔
724
        current = table[static_cast<std::size_t>(current)];
241,294✔
725
    }
726

727
    return chain;
4,967✔
UNCOV
728
}
×
729

730
sector_chain compound_document::allocate_short_sectors(std::size_t count)
4✔
731
{
732
    if (count == std::size_t(0)) return {};
4✔
733

734
    auto chain = sector_chain();
4✔
735
    auto current = allocate_short_sector();
4✔
736

737
    for (auto i = std::size_t(1); i < count; ++i)
76✔
738
    {
739
        chain.push_back(current);
72✔
740
        auto next = allocate_short_sector();
72✔
741
        ssat_[static_cast<std::size_t>(current)] = next;
72✔
742
        current = next;
72✔
743
    }
744

745
    chain.push_back(current);
4✔
746
    write_ssat();
4✔
747

748
    return chain;
4✔
749
}
4✔
750

751
sector_id compound_document::allocate_short_sector()
76✔
752
{
753
    const auto sectors_per_sector = sector_size() / sizeof(sector_id);
76✔
754
    auto next_free_iter = std::find(ssat_.begin(), ssat_.end(), FreeSector);
76✔
755

756
    if (next_free_iter == ssat_.end())
76✔
757
    {
758
        auto new_ssat_sector_id = allocate_sector();
4✔
759

760
        if (header_.ssat_start < 0)
4✔
761
        {
762
            header_.ssat_start = new_ssat_sector_id;
4✔
763
        }
764
        else
765
        {
766
            auto ssat_chain = follow_chain(header_.ssat_start, sat_);
×
767
            sat_[static_cast<std::size_t>(ssat_chain.back())] = new_ssat_sector_id;
×
768
            write_sat();
×
UNCOV
769
        }
×
770

771
        write_header();
4✔
772

773
        auto old_size = ssat_.size();
4✔
774
        ssat_.resize(old_size + sectors_per_sector, FreeSector);
4✔
775

776
        auto ssat_reader = binary_reader<sector_id>(ssat_);
4✔
777
        ssat_reader.offset(old_size / sectors_per_sector);
4✔
778
        write_sector(ssat_reader, new_ssat_sector_id);
4✔
779

780
        next_free_iter = std::find(ssat_.begin(), ssat_.end(), FreeSector);
4✔
781
    }
4✔
782

783
    ++header_.num_short_sectors;
76✔
784
    write_header();
76✔
785

786
    auto next_free = sector_id(next_free_iter - ssat_.begin());
76✔
787
    ssat_[static_cast<std::size_t>(next_free)] = EndOfChain;
76✔
788

789
    write_ssat();
76✔
790

791
    const auto short_sectors_per_sector = sector_size() / short_sector_size();
76✔
792
    const auto required_container_sectors = static_cast<std::size_t>(next_free) / short_sectors_per_sector + std::size_t(1);
76✔
793

794
    if (required_container_sectors > 0)
76✔
795
    {
796
        if (entries_[0].start < 0)
76✔
797
        {
798
            entries_[0].start = allocate_sector();
4✔
799
            write_entry(0);
4✔
800
        }
801

802
        auto container_chain = follow_chain(entries_[0].start, sat_);
76✔
803

804
        if (required_container_sectors > container_chain.size())
76✔
805
        {
806
            sat_[static_cast<std::size_t>(container_chain.back())] = allocate_sector();
8✔
807
            write_sat();
8✔
808
        }
809
    }
76✔
810

811
    return next_free;
76✔
812
}
813

814
directory_id compound_document::next_empty_entry()
12✔
815
{
816
    auto entry_id = directory_id(0);
12✔
817

818
    for (; entry_id < directory_id(entries_.size()); ++entry_id)
24✔
819
    {
820
        auto &entry = entries_[static_cast<std::size_t>(entry_id)];
20✔
821

822
        if (entry.type == compound_document_entry::entry_type::Empty)
20✔
823
        {
824
            return entry_id;
8✔
825
        }
826
    }
827

828
    // entry_id is now equal to entries_.size()
829

830
    if (header_.directory_start < 0)
4✔
831
    {
832
        header_.directory_start = allocate_sector();
4✔
833
    }
834
    else
835
    {
836
        auto directory_chain = follow_chain(header_.directory_start, sat_);
×
837
        sat_[static_cast<std::size_t>(directory_chain.back())] = allocate_sector();
×
838
        write_sat();
×
UNCOV
839
    }
×
840

841
    const auto entries_per_sector = sector_size()
4✔
842
        / sizeof(compound_document_entry);
4✔
843

844
    for (auto i = std::size_t(0); i < entries_per_sector; ++i)
20✔
845
    {
846
        auto empty_entry = compound_document_entry();
16✔
847
        empty_entry.type = compound_document_entry::entry_type::Empty;
16✔
848
        entries_.push_back(empty_entry);
16✔
849
        write_entry(entry_id + directory_id(i));
16✔
850
    }
851

852
    return entry_id;
4✔
853
}
854

855
directory_id compound_document::insert_entry(
12✔
856
    const std::string &name,
857
    compound_document_entry::entry_type type)
858
{
859
    auto entry_id = next_empty_entry();
12✔
860
    auto &entry = entries_[static_cast<std::size_t>(entry_id)];
12✔
861

862
    auto parent_id = directory_id(0);
12✔
863
    auto split = split_path(name);
12✔
864
    auto filename = split.back();
12✔
865
    split.pop_back();
12✔
866

867
    if (split.size() > 1)
12✔
868
    {
869
        parent_id = find_entry(join_path(split), compound_document_entry::entry_type::UserStorage);
×
870

871
        if (parent_id < 0)
×
872
        {
873
            throw xlnt::exception("bad path");
×
874
        }
875

876
        parent_storage_[entry_id] = parent_id;
×
877
    }
878

879
    entry.name(filename);
12✔
880
    entry.type = type;
12✔
881

882
    tree_insert(entry_id, parent_id);
12✔
883
    write_directory();
12✔
884

885
    return entry_id;
12✔
886
}
12✔
887

888
std::size_t compound_document::sector_data_start()
17,457✔
889
{
890
    return sizeof(compound_document_header);
17,457✔
891
}
892

893
bool compound_document::contains_entry(const std::string &path,
34✔
894
    compound_document_entry::entry_type type)
895
{
896
    return find_entry(path, type) >= 0;
34✔
897
}
898

899
directory_id compound_document::find_entry(const std::string &name,
60✔
900
    compound_document_entry::entry_type type)
901
{
902
    if (type == compound_document_entry::entry_type::RootStorage
60✔
903
        && (name == "/" || name == "/Root Entry")) return 0;
60✔
904

905
    auto entry_id = directory_id(0);
60✔
906

907
    for (auto &entry : entries_)
282✔
908
    {
909
        if (entry.type == type && tree_path(entry_id) == name)
274✔
910
        {
911
            return entry_id;
52✔
912
        }
913

914
        ++entry_id;
222✔
915
    }
916

917
    return End;
8✔
918
}
919

920
void compound_document::print_directory()
×
921
{
922
    auto entry_id = directory_id(0);
×
923

924
    for (auto &entry : entries_)
×
925
    {
926
        if (entry.type == compound_document_entry::entry_type::UserStream)
×
927
        {
928
            std::cout << tree_path(entry_id) << std::endl;
×
929
        }
930

931
        ++entry_id;
×
932
    }
933
}
×
934

935
void compound_document::write_directory()
998✔
936
{
937
    for (auto entry_id = std::size_t(0); entry_id < entries_.size(); ++entry_id)
4,990✔
938
    {
939
        write_entry(directory_id(entry_id));
3,992✔
940
    }
941
}
998✔
942

943
void compound_document::read_directory()
13✔
944
{
945
    const auto entries_per_sector = sector_size() / sizeof(compound_document_entry);
13✔
946
    const auto num_entries = follow_chain(header_.directory_start, sat_).size() * entries_per_sector;
13✔
947

948
    for (auto entry_id = std::size_t(0); entry_id < num_entries; ++entry_id)
145✔
949
    {
950
        entries_.push_back(compound_document_entry());
132✔
951
        read_entry(directory_id(entry_id));
132✔
952
    }
953

954
    auto stack = std::vector<directory_id>();
13✔
955
    auto storage_siblings = std::vector<directory_id>();
13✔
956
    auto stream_siblings = std::vector<directory_id>();
13✔
957

958
    auto directory_stack = std::vector<directory_id>();
13✔
959
    directory_stack.push_back(directory_id(0));
13✔
960

961
    while (!directory_stack.empty())
66✔
962
    {
963
        auto current_storage_id = directory_stack.back();
53✔
964
        directory_stack.pop_back();
53✔
965

966
        if (tree_child(current_storage_id) < 0) continue;
53✔
967

968
        auto storage_stack = std::vector<directory_id>();
53✔
969
        auto storage_root_id = tree_child(current_storage_id);
53✔
970
        parent_[storage_root_id] = End;
53✔
971
        storage_stack.push_back(storage_root_id);
53✔
972

973
        while (!storage_stack.empty())
159✔
974
        {
975
            auto current_entry_id = storage_stack.back();
106✔
976
            auto current_entry = entries_[static_cast<std::size_t>(current_entry_id)];
106✔
977
            storage_stack.pop_back();
106✔
978

979
            parent_storage_[current_entry_id] = current_storage_id;
106✔
980

981
            if (current_entry.type == compound_document_entry::entry_type::UserStorage)
106✔
982
            {
983
                directory_stack.push_back(current_entry_id);
40✔
984
            }
985

986
            if (tree_left(current_entry_id) >= 0)
106✔
987
            {
988
                storage_stack.push_back(tree_left(current_entry_id));
20✔
989
                tree_parent(tree_left(current_entry_id)) = current_entry_id;
20✔
990
            }
991

992
            if (tree_right(current_entry_id) >= 0)
106✔
993
            {
994
                storage_stack.push_back(tree_right(current_entry_id));
33✔
995
                tree_parent(tree_right(current_entry_id)) = current_entry_id;
33✔
996
            }
997
        }
998
    }
53✔
999
}
13✔
1000

1001
void compound_document::tree_insert(directory_id new_id, directory_id storage_id)
12✔
1002
{
1003
    using entry_color = compound_document_entry::entry_color;
1004

1005
    parent_storage_[new_id] = storage_id;
12✔
1006

1007
    tree_left(new_id) = End;
12✔
1008
    tree_right(new_id) = End;
12✔
1009

1010
    if (tree_root(new_id) == End)
12✔
1011
    {
1012
        if (new_id != 0)
8✔
1013
        {
1014
            tree_root(new_id) = new_id;
4✔
1015
        }
1016

1017
        tree_color(new_id) = entry_color::Black;
8✔
1018
        tree_parent(new_id) = End;
8✔
1019

1020
        return;
8✔
1021
    }
1022

1023
    // normal tree insert
1024
    // (will probably unbalance the tree, fix after)
1025
    auto x = tree_root(new_id);
4✔
1026
    auto y = End;
4✔
1027

1028
    while (x >= 0)
8✔
1029
    {
1030
        y = x;
4✔
1031

1032
        if (compare_keys(tree_key(new_id), tree_key(x)) > 0)
4✔
1033
        {
1034
            x = tree_right(x);
×
1035
        }
1036
        else
1037
        {
1038
            x = tree_left(x);
4✔
1039
        }
1040
    }
1041

1042
    tree_parent(new_id) = y;
4✔
1043

1044
    if (compare_keys(tree_key(new_id), tree_key(y)) > 0)
4✔
1045
    {
1046
        tree_right(y) = new_id;
×
1047
    }
1048
    else
1049
    {
1050
        tree_left(y) = new_id;
4✔
1051
    }
1052

1053
    tree_insert_fixup(new_id);
4✔
1054
}
1055

1056
std::string compound_document::tree_path(directory_id id)
138✔
1057
{
1058
    auto storage_id = parent_storage_[id];
138✔
1059
    auto result = std::vector<std::string>();
138✔
1060

1061
    while (storage_id > 0)
236✔
1062
    {
1063
        storage_id = parent_storage_[storage_id];
98✔
1064
        result.push_back(entries_[static_cast<std::size_t>(storage_id)].name());
98✔
1065
    }
1066

1067
    return "/" + join_path(result) + entries_[static_cast<std::size_t>(id)].name();
276✔
1068
}
138✔
1069

1070
void compound_document::tree_rotate_left(directory_id x)
×
1071
{
1072
    auto y = tree_right(x);
×
1073

1074
    // turn y's left subtree into x's right subtree
1075
    tree_right(x) = tree_left(y);
×
1076

1077
    if (tree_left(y) != End)
×
1078
    {
1079
        tree_parent(tree_left(y)) = x;
×
1080
    }
1081

1082
    // link x's parent to y
1083
    tree_parent(y) = tree_parent(x);
×
1084

1085
    if (tree_parent(x) == End)
×
1086
    {
1087
        tree_root(x) = y;
×
1088
    }
1089
    else if (x == tree_left(tree_parent(x)))
×
1090
    {
1091
        tree_left(tree_parent(x)) = y;
×
1092
    }
1093
    else
1094
    {
1095
        tree_right(tree_parent(x)) = y;
×
1096
    }
1097

1098
    // put x on y's left
1099
    tree_left(y) = x;
×
1100
    tree_parent(x) = y;
×
1101
}
×
1102

1103
void compound_document::tree_rotate_right(directory_id y)
×
1104
{
1105
    auto x = tree_left(y);
×
1106

1107
    // turn x's right subtree into y's left subtree
1108
    tree_left(y) = tree_right(x);
×
1109

1110
    if (tree_right(x) != End)
×
1111
    {
1112
        tree_parent(tree_right(x)) = y;
×
1113
    }
1114

1115
    // link y's parent to x
1116
    tree_parent(x) = tree_parent(y);
×
1117

1118
    if (tree_parent(y) == End)
×
1119
    {
1120
        tree_root(y) = x;
×
1121
    }
1122
    else if (y == tree_left(tree_parent(y)))
×
1123
    {
1124
        tree_left(tree_parent(y)) = x;
×
1125
    }
1126
    else
1127
    {
1128
        tree_right(tree_parent(y)) = x;
×
1129
    }
1130

1131
    // put y on x's right
1132
    tree_right(x) = y;
×
1133
    tree_parent(y) = x;
×
1134
}
×
1135

1136
void compound_document::tree_insert_fixup(directory_id x)
4✔
1137
{
1138
    using entry_color = compound_document_entry::entry_color;
1139

1140
    tree_color(x) = entry_color::Red;
4✔
1141

1142
    while (x != tree_root(x) && tree_color(tree_parent(x)) == entry_color::Red)
4✔
1143
    {
1144
        if (tree_parent(x) == tree_left(tree_parent(tree_parent(x))))
×
1145
        {
1146
            auto y = tree_right(tree_parent(tree_parent(x)));
×
1147

1148
            if (y >= 0 && tree_color(y) == entry_color::Red)
×
1149
            {
1150
                // case 1
1151
                tree_color(tree_parent(x)) = entry_color::Black;
×
1152
                tree_color(y) = entry_color::Black;
×
1153
                tree_color(tree_parent(tree_parent(x))) = entry_color::Red;
×
1154
                x = tree_parent(tree_parent(x));
×
1155
            }
1156
            else
1157
            {
1158
                if (x == tree_right(tree_parent(x)))
×
1159
                {
1160
                    // case 2
1161
                    x = tree_parent(x);
×
1162
                    tree_rotate_left(x);
×
1163
                }
1164

1165
                // case 3
1166
                tree_color(tree_parent(x)) = entry_color::Black;
×
1167
                tree_color(tree_parent(tree_parent(x))) = entry_color::Red;
×
1168
                tree_rotate_right(tree_parent(tree_parent(x)));
×
1169
            }
1170
        }
1171
        else // same as above with left and right switched
1172
        {
1173
            auto y = tree_left(tree_parent(tree_parent(x)));
×
1174

1175
            if (y >= 0 && tree_color(y) == entry_color::Red)
×
1176
            {
1177
                //case 1
1178
                tree_color(tree_parent(x)) = entry_color::Black;
×
1179
                tree_color(y) = entry_color::Black;
×
1180
                tree_color(tree_parent(tree_parent(x))) = entry_color::Red;
×
1181
                x = tree_parent(tree_parent(x));
×
1182
            }
1183
            else
1184
            {
1185
                if (x == tree_left(tree_parent(x)))
×
1186
                {
1187
                    // case 2
1188
                    x = tree_parent(x);
×
1189
                    tree_rotate_right(x);
×
1190
                }
1191

1192
                // case 3
1193
                tree_color(tree_parent(x)) = entry_color::Black;
×
1194
                tree_color(tree_parent(tree_parent(x))) = entry_color::Red;
×
1195
                tree_rotate_left(tree_parent(tree_parent(x)));
×
1196
            }
1197
        }
1198
    }
1199

1200
    tree_color(tree_root(x)) = entry_color::Black;
4✔
1201
}
4✔
1202

1203
directory_id &compound_document::tree_left(directory_id id)
166✔
1204
{
1205
    return entries_[static_cast<std::size_t>(id)].prev;
166✔
1206
}
1207

1208
directory_id &compound_document::tree_right(directory_id id)
184✔
1209
{
1210
    return entries_[static_cast<std::size_t>(id)].next;
184✔
1211
}
1212

1213
directory_id &compound_document::tree_parent(directory_id id)
69✔
1214
{
1215
    return parent_[id];
69✔
1216
}
1217

1218
directory_id &compound_document::tree_root(directory_id id)
28✔
1219
{
1220
    return tree_child(parent_storage_[id]);
28✔
1221
}
1222

1223
directory_id &compound_document::tree_child(directory_id id)
134✔
1224
{
1225
    return entries_[static_cast<std::size_t>(id)].child;
134✔
1226
}
1227

1228
std::string compound_document::tree_key(directory_id id)
16✔
1229
{
1230
    return entries_[static_cast<std::size_t>(id)].name();
16✔
1231
}
1232

1233
compound_document_entry::entry_color &compound_document::tree_color(directory_id id)
20✔
1234
{
1235
    return entries_[static_cast<std::size_t>(id)].color;
20✔
1236
}
1237

1238
void compound_document::read_header()
13✔
1239
{
1240
    in_->seekg(0, std::ios::beg);
13✔
1241
    in_->read(reinterpret_cast<char *>(&header_), sizeof(compound_document_header));
13✔
1242
}
13✔
1243

1244
void compound_document::read_msat()
13✔
1245
{
1246
    msat_.clear();
13✔
1247

1248
    auto msat_sector = header_.extra_msat_start;
13✔
1249
    auto msat_writer = binary_writer<sector_id>(msat_);
13✔
1250

1251
    for (auto i = std::uint32_t(0); i < header_.num_msat_sectors; ++i)
55✔
1252
    {
1253
        if (i < std::uint32_t(109))
42✔
1254
        {
1255
            msat_writer.write(header_.msat[i]);
42✔
1256
        }
1257
        else
1258
        {
1259
            read_sector(msat_sector, msat_writer);
×
1260

1261
            msat_sector = msat_.back();
×
1262
            msat_.pop_back();
×
1263
        }
1264
    }
1265
}
13✔
1266

1267
void compound_document::read_sat()
13✔
1268
{
1269
    sat_.clear();
13✔
1270
    auto sat_writer = binary_writer<sector_id>(sat_);
13✔
1271

1272
    for (auto msat_sector : msat_)
55✔
1273
    {
1274
        read_sector(msat_sector, sat_writer);
42✔
1275
    }
1276
}
13✔
1277

1278
void compound_document::read_ssat()
13✔
1279
{
1280
    ssat_.clear();
13✔
1281
    auto ssat_writer = binary_writer<sector_id>(ssat_);
13✔
1282

1283
    for (auto ssat_sector : follow_chain(header_.ssat_start, sat_))
26✔
1284
    {
1285
        read_sector(ssat_sector, ssat_writer);
13✔
1286
    }
13✔
1287
}
13✔
1288

1289
void compound_document::read_entry(directory_id id)
132✔
1290
{
1291
    const auto directory_chain = follow_chain(header_.directory_start, sat_);
132✔
1292
    const auto entries_per_sector = sector_size() / sizeof(compound_document_entry);
132✔
1293
    const auto directory_sector = directory_chain[static_cast<std::size_t>(id) / entries_per_sector];
132✔
1294
    const auto offset = sector_size() * static_cast<std::size_t>(directory_sector)
132✔
1295
        + ((static_cast<std::size_t>(id) % entries_per_sector) * sizeof(compound_document_entry));
132✔
1296

1297
    in_->seekg(static_cast<std::ptrdiff_t>(sector_data_start() + offset), std::ios::beg);
132✔
1298
    in_->read(reinterpret_cast<char *>(&entries_[static_cast<std::size_t>(id)]), sizeof(compound_document_entry));
132✔
1299
}
132✔
1300

1301
void compound_document::write_header()
95✔
1302
{
1303
    out_->seekp(0, std::ios::beg);
95✔
1304
    out_->write(reinterpret_cast<char *>(&header_), sizeof(compound_document_header));
95✔
1305
}
95✔
1306

1307
void compound_document::write_msat()
11✔
1308
{
1309
    auto msat_sector = header_.extra_msat_start;
11✔
1310

1311
    for (auto i = std::uint32_t(0); i < header_.num_msat_sectors; ++i)
39✔
1312
    {
1313
        if (i < std::uint32_t(109))
28✔
1314
        {
1315
            header_.msat[i] = msat_[i];
28✔
1316
        }
1317
        else
1318
        {
1319
            auto sector = std::vector<sector_id>();
×
1320
            auto sector_writer = binary_writer<sector_id>(sector);
×
1321

1322
            read_sector(msat_sector, sector_writer);
×
1323

1324
            msat_sector = sector.back();
×
1325
            sector.pop_back();
×
1326

1327
            std::copy(sector.begin(), sector.end(), std::back_inserter(msat_));
×
UNCOV
1328
        }
×
1329
    }
1330
}
11✔
1331

1332
void compound_document::write_sat()
2,012✔
1333
{
1334
    auto sector_reader = binary_reader<sector_id>(sat_);
2,012✔
1335

1336
    for (auto sat_sector : msat_)
10,464✔
1337
    {
1338
        write_sector(sector_reader, sat_sector);
8,452✔
1339
    }
1340
}
2,012✔
1341

1342
void compound_document::write_ssat()
80✔
1343
{
1344
    auto sector_reader = binary_reader<sector_id>(ssat_);
80✔
1345

1346
    for (auto ssat_sector : follow_chain(header_.ssat_start, sat_))
160✔
1347
    {
1348
        write_sector(sector_reader, ssat_sector);
80✔
1349
    }
80✔
1350
}
80✔
1351

1352
void compound_document::write_entry(directory_id id)
4,012✔
1353
{
1354
    const auto directory_chain = follow_chain(header_.directory_start, sat_);
4,012✔
1355
    const auto entries_per_sector = sector_size() / sizeof(compound_document_entry);
4,012✔
1356
    const auto directory_sector = directory_chain[static_cast<std::size_t>(id) / entries_per_sector];
4,012✔
1357
    const auto offset = sector_data_start() + sector_size() * static_cast<std::size_t>(directory_sector)
4,012✔
1358
        + ((static_cast<std::size_t>(id) % entries_per_sector) * sizeof(compound_document_entry));
4,012✔
1359

1360
    out_->seekp(static_cast<std::ptrdiff_t>(offset), std::ios::beg);
4,012✔
1361
    out_->write(reinterpret_cast<char *>(&entries_[static_cast<std::size_t>(id)]), sizeof(compound_document_entry));
4,012✔
1362
}
4,012✔
1363

1364
} // namespace detail
1365
} // namespace xlnt
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc