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