1 /* -*- Mode: C++ -*- */ 2 class Block; 3 class BlockIterator; 4 class TmpFile; 5 6 class Block { 7 public: Block()8 Block() 9 : data_(NULL), 10 data_size_(0), 11 size_(0) { } 12 ~Block()13 ~Block() { 14 if (data_) { 15 delete [] data_; 16 } 17 } 18 Size()19 size_t Size() const { 20 return size_; 21 } 22 23 uint8_t operator[](size_t i) const { 24 CHECK_LT(i, size_); 25 return data_[i]; 26 } 27 Data()28 uint8_t* Data() const { 29 if (data_ == NULL) { 30 CHECK_EQ(0, size_); 31 data_size_ = 1; 32 data_ = new uint8_t[1]; 33 } 34 return data_; 35 } 36 37 // For writing to blocks Append(const uint8_t * data,size_t size)38 void Append(const uint8_t *data, size_t size) { 39 if (data_ == NULL) { 40 CHECK_EQ(0, size_); 41 CHECK_EQ(0, data_size_); 42 data_ = new uint8_t[Constants::BLOCK_SIZE]; 43 data_size_ = Constants::BLOCK_SIZE; 44 } 45 46 if (size_ + size > data_size_) { 47 uint8_t *tmp = data_; 48 while (size_ + size > data_size_) { 49 data_size_ *= 2; 50 } 51 data_ = new uint8_t[data_size_]; 52 memcpy(data_, tmp, size_); 53 delete [] tmp; 54 } 55 56 memcpy(data_ + size_, data, size); 57 size_ += size; 58 } 59 60 // For cleaing a block Reset()61 void Reset() { 62 size_ = 0; 63 } 64 65 // Note: This does not benefit from -Wformat= checking, due to the 66 // enclosing template. Further, it was not used. 67 // void Print() const { 68 // xoff_t pos = 0; 69 // for (size_t i = 0; i < Size(); i++) { 70 // if (pos % 16 == 0) { 71 // DP(RINT "%5" Q "x: ", pos); 72 // } 73 // DP(RINT "%02x ", (*this)[i]); 74 // if (pos % 16 == 15) { 75 // DP(RINT "\n"); 76 // } 77 // pos++; 78 // } 79 // DP(RINT "\n"); 80 // } 81 WriteTmpFile(TmpFile * f)82 void WriteTmpFile(TmpFile *f) const { 83 f->Append(this); 84 } 85 SetSize(size_t size)86 void SetSize(size_t size) { 87 uint8_t *t = NULL; 88 if (data_size_ < size) { 89 if (data_) { 90 t = data_; 91 } 92 data_ = new uint8_t[size]; 93 data_size_ = size; 94 } 95 if (t && size < size_) { 96 memcpy(data_, t, size); 97 } 98 delete [] t; 99 size_ = size; 100 } 101 102 private: 103 friend class BlockIterator; 104 105 mutable uint8_t *data_; 106 mutable size_t data_size_; 107 size_t size_; 108 }; 109 110 class FileSpec { 111 public: FileSpec(MTRandom * rand)112 FileSpec(MTRandom *rand) 113 : rand_(rand) { 114 } 115 116 // Generates a file with a known size GenerateFixedSize(xoff_t size)117 void GenerateFixedSize(xoff_t size) { 118 Reset(); 119 120 for (xoff_t p = 0; p < size; ) { 121 xoff_t t = min(Constants::BLOCK_SIZE, size - p); 122 table_.insert(make_pair(p, Segment(t, rand_))); 123 p += t; 124 } 125 } 126 127 // Generates a file with exponential-random distributed size GenerateRandomSize(xoff_t mean)128 void GenerateRandomSize(xoff_t mean) { 129 GenerateFixedSize(rand_->ExpRand(mean)); 130 } 131 132 // Returns the size of the file Size()133 xoff_t Size() const { 134 if (table_.empty()) { 135 return 0; 136 } 137 ConstSegmentMapIterator i = --table_.end(); 138 return i->first + i->second.Size(); 139 } 140 141 // Returns the number of blocks 142 xoff_t Blocks(size_t blksize = Constants::BLOCK_SIZE) const { 143 if (table_.empty()) { 144 return 0; 145 } 146 return ((Size() - 1) / blksize) + 1; 147 } 148 149 // Returns the number of segments Segments()150 xoff_t Segments() const { 151 return table_.size(); 152 } 153 154 // Create a mutation according to "what". ModifyTo(const Mutator & mutator,FileSpec * modify)155 void ModifyTo(const Mutator &mutator, 156 FileSpec *modify) const { 157 modify->Reset(); 158 mutator.Mutate(&modify->table_, &table_, rand_); 159 modify->CheckSegments(); 160 } 161 CheckSegments()162 void CheckSegments() const { 163 for (ConstSegmentMapIterator iter(table_.begin()); 164 iter != table_.end(); ) { 165 ConstSegmentMapIterator iter0(iter++); 166 if (iter == table_.end()) { 167 break; 168 } 169 CHECK_EQ(iter0->first + iter0->second.Size(), iter->first); 170 } 171 } 172 Reset()173 void Reset() { 174 table_.clear(); 175 } 176 Print()177 void Print() const { 178 for (ConstSegmentMapIterator iter(table_.begin()); 179 iter != table_.end(); 180 ++iter) { 181 const Segment &seg = iter->second; 182 cerr << "Segment at " << iter->first 183 << " (" << seg.ToString() << ")" << endl; 184 } 185 } 186 PrintData()187 void PrintData() const { 188 Block block; 189 for (BlockIterator iter(*this); !iter.Done(); iter.Next()) { 190 iter.Get(&block); 191 block.Print(); 192 } 193 } 194 WriteTmpFile(TmpFile * f)195 void WriteTmpFile(TmpFile *f) const { 196 Block block; 197 for (BlockIterator iter(*this); !iter.Done(); iter.Next()) { 198 iter.Get(&block); 199 f->Append(&block); 200 } 201 } 202 Get(Block * block,xoff_t offset,size_t size)203 void Get(Block *block, xoff_t offset, size_t size) const { 204 size_t got = 0; 205 block->SetSize(size); 206 207 ConstSegmentMapIterator pos = table_.upper_bound(offset); 208 if (pos == table_.begin()) { 209 CHECK_EQ(0, Size()); 210 return; 211 } 212 --pos; 213 214 while (got < size) { 215 CHECK(pos != table_.end()); 216 CHECK_GE(offset, pos->first); 217 218 const Segment &seg = pos->second; 219 220 // The position of this segment may start before this block starts, 221 // and then the position of the data may be offset from the seeding 222 // position. 223 size_t seg_offset = offset - pos->first; 224 size_t advance = min(seg.Size() - seg_offset, 225 size - got); 226 227 seg.Fill(seg_offset, advance, block->Data() + got); 228 229 got += advance; 230 offset += advance; 231 ++pos; 232 } 233 } 234 235 typedef BlockIterator iterator; 236 237 private: 238 friend class BlockIterator; 239 240 MTRandom *rand_; 241 SegmentMap table_; 242 }; 243 244 class BlockIterator { 245 public: BlockIterator(const FileSpec & spec)246 explicit BlockIterator(const FileSpec& spec) 247 : spec_(spec), 248 blkno_(0), 249 blksize_(Constants::BLOCK_SIZE) { } 250 BlockIterator(const FileSpec & spec,size_t blksize)251 BlockIterator(const FileSpec& spec, 252 size_t blksize) 253 : spec_(spec), 254 blkno_(0), 255 blksize_(blksize) { } 256 Done()257 bool Done() const { 258 return blkno_ >= spec_.Blocks(blksize_); 259 } 260 Next()261 void Next() { 262 blkno_++; 263 } 264 Blkno()265 xoff_t Blkno() const { 266 return blkno_; 267 } 268 Blocks()269 xoff_t Blocks() const { 270 return spec_.Blocks(blksize_); 271 } 272 Offset()273 xoff_t Offset() const { 274 return blkno_ * blksize_; 275 } 276 SetBlock(xoff_t blkno)277 void SetBlock(xoff_t blkno) { 278 CHECK_LE(blkno, Blocks()); 279 blkno_ = blkno; 280 } 281 Get(Block * block)282 void Get(Block *block) const { 283 spec_.Get(block, blkno_ * blksize_, BytesOnBlock()); 284 } 285 BytesOnBlock()286 size_t BytesOnBlock() const { 287 xoff_t blocks = spec_.Blocks(blksize_); 288 xoff_t size = spec_.Size(); 289 290 DCHECK((blkno_ < blocks) || 291 (blkno_ == blocks && size % blksize_ == 0)); 292 293 if (blkno_ == blocks) { 294 return 0; 295 } 296 if (blkno_ + 1 == blocks) { 297 return ((size - 1) % blksize_) + 1; 298 } 299 return blksize_; 300 } 301 BlockSize()302 size_t BlockSize() const { 303 return blksize_; 304 } 305 306 private: 307 const FileSpec& spec_; 308 xoff_t blkno_; 309 size_t blksize_; 310 }; 311 312 class ExtFile { 313 public: ExtFile()314 ExtFile() { 315 static int static_counter = 0; 316 pid_t pid = getpid(); 317 char buf[64]; 318 xoff_t xpid = pid; 319 snprintf(buf, 64, "/tmp/regtest.%" Q "u.%d", xpid, static_counter++); 320 filename_.append(buf); 321 unlink(filename_.c_str()); 322 } 323 ~ExtFile()324 ~ExtFile() { 325 unlink(filename_.c_str()); 326 } 327 Name()328 const char* Name() const { 329 return filename_.c_str(); 330 } 331 332 // Check whether a real file matches a file spec. EqualsSpec(const FileSpec & spec)333 bool EqualsSpec(const FileSpec &spec) const { 334 main_file t; 335 main_file_init(&t); 336 CHECK_EQ(0, main_file_open(&t, Name(), XO_READ)); 337 338 Block tblock; 339 Block sblock; 340 for (BlockIterator iter(spec); !iter.Done(); iter.Next()) { 341 iter.Get(&sblock); 342 tblock.SetSize(sblock.Size()); 343 size_t tread; 344 CHECK_EQ(0, main_file_read(&t, 345 tblock.Data(), 346 tblock.Size(), &tread, "read failed")); 347 CHECK_EQ(0, CmpDifferentBlockBytes(tblock, sblock)); 348 } 349 350 CHECK_EQ(0, main_file_close(&t)); 351 main_file_cleanup(&t); 352 return true; 353 } 354 355 protected: 356 string filename_; 357 }; 358 359 class TmpFile : public ExtFile { 360 public: TmpFile()361 TmpFile() { 362 main_file_init(&file_); 363 CHECK_EQ(0, main_file_open(&file_, Name(), XO_WRITE)); 364 } 365 ~TmpFile()366 ~TmpFile() { 367 main_file_cleanup(&file_); 368 } 369 Append(const Block * block)370 void Append(const Block *block) { 371 CHECK_EQ(0, main_file_write(&file_, 372 block->Data(), block->Size(), 373 "tmpfile write failed")); 374 } 375 Name()376 const char* Name() const { 377 if (main_file_isopen(&file_)) { 378 CHECK_EQ(0, main_file_close(&file_)); 379 } 380 return ExtFile::Name(); 381 } 382 383 private: 384 mutable main_file file_; 385 }; 386