123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- /* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
- Copyright 2016 Joshua MacDonald
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- class Block;
- class BlockIterator;
- class TmpFile;
- class Block {
- public:
- Block()
- : data_(NULL),
- data_size_(0),
- size_(0) { }
- ~Block() {
- if (data_) {
- delete [] data_;
- }
- }
- size_t Size() const {
- return size_;
- }
- uint8_t operator[](size_t i) const {
- CHECK_LT(i, size_);
- return data_[i];
- }
- uint8_t* Data() const {
- if (data_ == NULL) {
- CHECK_EQ(0, size_);
- data_size_ = 1;
- data_ = new uint8_t[1];
- }
- return data_;
- }
- // For writing to blocks
- void Append(const uint8_t *data, size_t size) {
- if (data_ == NULL) {
- CHECK_EQ(0, size_);
- CHECK_EQ(0, data_size_);
- data_ = new uint8_t[Constants::BLOCK_SIZE];
- data_size_ = Constants::BLOCK_SIZE;
- }
- if (size_ + size > data_size_) {
- uint8_t *tmp = data_;
- while (size_ + size > data_size_) {
- data_size_ *= 2;
- }
- data_ = new uint8_t[data_size_];
- memcpy(data_, tmp, size_);
- delete [] tmp;
- }
- memcpy(data_ + size_, data, size);
- size_ += size;
- }
- // For cleaing a block
- void Reset() {
- size_ = 0;
- }
- // Note: This does not benefit from -Wformat= checking, due to the
- // enclosing template. Further, it was not used.
- // void Print() const {
- // xoff_t pos = 0;
- // for (size_t i = 0; i < Size(); i++) {
- // if (pos % 16 == 0) {
- // DP(RINT "%5" Q "x: ", pos);
- // }
- // DP(RINT "%02x ", (*this)[i]);
- // if (pos % 16 == 15) {
- // DP(RINT "\n");
- // }
- // pos++;
- // }
- // DP(RINT "\n");
- // }
- void WriteTmpFile(TmpFile *f) const {
- f->Append(this);
- }
- void SetSize(size_t size) {
- uint8_t *t = NULL;
- if (data_size_ < size) {
- if (data_) {
- t = data_;
- }
- data_ = new uint8_t[size];
- data_size_ = size;
- }
- if (t && size < size_) {
- memcpy(data_, t, size);
- }
- delete [] t;
- size_ = size;
- }
- private:
- friend class BlockIterator;
- mutable uint8_t *data_;
- mutable size_t data_size_;
- size_t size_;
- };
- class FileSpec {
- public:
- FileSpec(MTRandom *rand)
- : rand_(rand) {
- }
- // Generates a file with a known size
- void GenerateFixedSize(xoff_t size) {
- Reset();
- for (xoff_t p = 0; p < size; ) {
- xoff_t t = min(Constants::BLOCK_SIZE, size - p);
- table_.insert(make_pair(p, Segment(t, rand_)));
- p += t;
- }
- }
- // Generates a file with exponential-random distributed size
- void GenerateRandomSize(xoff_t mean) {
- GenerateFixedSize(rand_->ExpRand(mean));
- }
- // Returns the size of the file
- xoff_t Size() const {
- if (table_.empty()) {
- return 0;
- }
- ConstSegmentMapIterator i = --table_.end();
- return i->first + i->second.Size();
- }
- // Returns the number of blocks
- xoff_t Blocks(size_t blksize = Constants::BLOCK_SIZE) const {
- if (table_.empty()) {
- return 0;
- }
- return ((Size() - 1) / blksize) + 1;
- }
- // Returns the number of segments
- xoff_t Segments() const {
- return table_.size();
- }
- // Create a mutation according to "what".
- void ModifyTo(const Mutator &mutator,
- FileSpec *modify) const {
- modify->Reset();
- mutator.Mutate(&modify->table_, &table_, rand_);
- modify->CheckSegments();
- }
- void CheckSegments() const {
- for (ConstSegmentMapIterator iter(table_.begin());
- iter != table_.end(); ) {
- ConstSegmentMapIterator iter0(iter++);
- if (iter == table_.end()) {
- break;
- }
- CHECK_EQ(iter0->first + iter0->second.Size(), iter->first);
- }
- }
- void Reset() {
- table_.clear();
- }
- void Print() const {
- for (ConstSegmentMapIterator iter(table_.begin());
- iter != table_.end();
- ++iter) {
- const Segment &seg = iter->second;
- cerr << "Segment at " << iter->first
- << " (" << seg.ToString() << ")" << endl;
- }
- }
- void PrintData() const {
- Block block;
- for (BlockIterator iter(*this); !iter.Done(); iter.Next()) {
- iter.Get(&block);
- block.Print();
- }
- }
- void WriteTmpFile(TmpFile *f) const {
- Block block;
- for (BlockIterator iter(*this); !iter.Done(); iter.Next()) {
- iter.Get(&block);
- f->Append(&block);
- }
- }
- void Get(Block *block, xoff_t offset, size_t size) const {
- size_t got = 0;
- block->SetSize(size);
- ConstSegmentMapIterator pos = table_.upper_bound(offset);
- if (pos == table_.begin()) {
- CHECK_EQ(0, Size());
- return;
- }
- --pos;
- while (got < size) {
- CHECK(pos != table_.end());
- CHECK_GE(offset, pos->first);
- const Segment &seg = pos->second;
- // The position of this segment may start before this block starts,
- // and then the position of the data may be offset from the seeding
- // position.
- size_t seg_offset = offset - pos->first;
- size_t advance = min(seg.Size() - seg_offset,
- size - got);
- seg.Fill(seg_offset, advance, block->Data() + got);
- got += advance;
- offset += advance;
- ++pos;
- }
- }
- typedef BlockIterator iterator;
- private:
- friend class BlockIterator;
- MTRandom *rand_;
- SegmentMap table_;
- };
- class BlockIterator {
- public:
- explicit BlockIterator(const FileSpec& spec)
- : spec_(spec),
- blkno_(0),
- blksize_(Constants::BLOCK_SIZE) { }
- BlockIterator(const FileSpec& spec,
- size_t blksize)
- : spec_(spec),
- blkno_(0),
- blksize_(blksize) { }
- bool Done() const {
- return blkno_ >= spec_.Blocks(blksize_);
- }
- void Next() {
- blkno_++;
- }
- xoff_t Blkno() const {
- return blkno_;
- }
- xoff_t Blocks() const {
- return spec_.Blocks(blksize_);
- }
- xoff_t Offset() const {
- return blkno_ * blksize_;
- }
- void SetBlock(xoff_t blkno) {
- CHECK_LE(blkno, Blocks());
- blkno_ = blkno;
- }
- void Get(Block *block) const {
- spec_.Get(block, blkno_ * blksize_, BytesOnBlock());
- }
- size_t BytesOnBlock() const {
- xoff_t blocks = spec_.Blocks(blksize_);
- xoff_t size = spec_.Size();
- DCHECK((blkno_ < blocks) ||
- (blkno_ == blocks && size % blksize_ == 0));
- if (blkno_ == blocks) {
- return 0;
- }
- if (blkno_ + 1 == blocks) {
- return ((size - 1) % blksize_) + 1;
- }
- return blksize_;
- }
- size_t BlockSize() const {
- return blksize_;
- }
- private:
- const FileSpec& spec_;
- xoff_t blkno_;
- size_t blksize_;
- };
- class ExtFile {
- public:
- ExtFile() {
- static int static_counter = 0;
- pid_t pid = getpid();
- char buf[64];
- xoff_t xpid = pid;
- snprintf(buf, 64, "/tmp/regtest.%" Q "u.%d", xpid, static_counter++);
- filename_.append(buf);
- unlink(filename_.c_str());
- }
- ~ExtFile() {
- unlink(filename_.c_str());
- }
- const char* Name() const {
- return filename_.c_str();
- }
- // Check whether a real file matches a file spec.
- bool EqualsSpec(const FileSpec &spec) const {
- main_file t;
- main_file_init(&t);
- CHECK_EQ(0, main_file_open(&t, Name(), XO_READ));
- Block tblock;
- Block sblock;
- for (BlockIterator iter(spec); !iter.Done(); iter.Next()) {
- iter.Get(&sblock);
- tblock.SetSize(sblock.Size());
- size_t tread;
- CHECK_EQ(0, main_file_read(&t,
- tblock.Data(),
- tblock.Size(), &tread, "read failed"));
- CHECK_EQ(0, CmpDifferentBlockBytes(tblock, sblock));
- }
- CHECK_EQ(0, main_file_close(&t));
- main_file_cleanup(&t);
- return true;
- }
- protected:
- string filename_;
- };
- class TmpFile : public ExtFile {
- public:
- TmpFile() {
- main_file_init(&file_);
- CHECK_EQ(0, main_file_open(&file_, Name(), XO_WRITE));
- }
- ~TmpFile() {
- main_file_cleanup(&file_);
- }
- void Append(const Block *block) {
- CHECK_EQ(0, main_file_write(&file_,
- block->Data(), block->Size(),
- "tmpfile write failed"));
- }
- const char* Name() const {
- if (main_file_isopen(&file_)) {
- CHECK_EQ(0, main_file_close(&file_));
- }
- return ExtFile::Name();
- }
- private:
- mutable main_file file_;
- };
|