leveldb-ruby 0.7 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/README +1 -1
  2. data/leveldb/Makefile +70 -29
  3. data/leveldb/build_detect_platform +74 -0
  4. data/leveldb/db/builder.cc +2 -4
  5. data/leveldb/db/builder.h +4 -6
  6. data/leveldb/db/c.cc +471 -0
  7. data/leveldb/db/corruption_test.cc +21 -16
  8. data/leveldb/db/db_bench.cc +400 -200
  9. data/leveldb/db/db_impl.cc +276 -131
  10. data/leveldb/db/db_impl.h +22 -10
  11. data/leveldb/db/db_iter.cc +2 -1
  12. data/leveldb/db/db_test.cc +391 -43
  13. data/leveldb/db/dbformat.cc +31 -0
  14. data/leveldb/db/dbformat.h +51 -1
  15. data/leveldb/db/filename.h +1 -1
  16. data/leveldb/db/log_format.h +1 -1
  17. data/leveldb/db/log_reader.cc +16 -11
  18. data/leveldb/db/memtable.cc +37 -0
  19. data/leveldb/db/memtable.h +6 -0
  20. data/leveldb/db/repair.cc +17 -14
  21. data/leveldb/db/skiplist_test.cc +2 -2
  22. data/leveldb/db/version_edit.cc +7 -9
  23. data/leveldb/db/version_edit.h +2 -1
  24. data/leveldb/db/version_set.cc +416 -104
  25. data/leveldb/db/version_set.h +78 -14
  26. data/leveldb/db/version_set_test.cc +179 -0
  27. data/leveldb/db/write_batch_internal.h +2 -0
  28. data/leveldb/include/leveldb/c.h +246 -0
  29. data/leveldb/include/leveldb/db.h +14 -2
  30. data/leveldb/include/leveldb/env.h +31 -10
  31. data/leveldb/include/leveldb/options.h +7 -18
  32. data/leveldb/include/leveldb/slice.h +2 -2
  33. data/leveldb/include/leveldb/status.h +1 -1
  34. data/leveldb/port/atomic_pointer.h +144 -0
  35. data/leveldb/port/port.h +0 -2
  36. data/leveldb/port/port_android.h +7 -1
  37. data/leveldb/port/port_example.h +11 -1
  38. data/leveldb/port/port_posix.h +56 -38
  39. data/leveldb/table/format.cc +12 -8
  40. data/leveldb/table/table_test.cc +16 -7
  41. data/leveldb/util/cache.cc +173 -100
  42. data/leveldb/util/cache_test.cc +28 -11
  43. data/leveldb/util/coding.h +4 -4
  44. data/leveldb/util/comparator.cc +1 -0
  45. data/leveldb/util/env.cc +10 -5
  46. data/leveldb/util/env_posix.cc +48 -87
  47. data/leveldb/util/histogram.cc +11 -0
  48. data/leveldb/util/histogram.h +1 -0
  49. data/leveldb/util/posix_logger.h +98 -0
  50. data/leveldb/util/testharness.cc +12 -0
  51. data/leveldb/util/testharness.h +10 -1
  52. data/lib/leveldb.rb +11 -3
  53. metadata +41 -22
@@ -32,7 +32,7 @@ class CacheTest {
32
32
  current_->deleted_values_.push_back(DecodeValue(v));
33
33
  }
34
34
 
35
- static const int kCacheSize = 100;
35
+ static const int kCacheSize = 1000;
36
36
  std::vector<int> deleted_keys_;
37
37
  std::vector<int> deleted_values_;
38
38
  Cache* cache_;
@@ -137,23 +137,40 @@ TEST(CacheTest, EvictionPolicy) {
137
137
  Insert(200, 201);
138
138
 
139
139
  // Frequently used entry must be kept around
140
- for (int i = 0; i < kCacheSize; i++) {
140
+ for (int i = 0; i < kCacheSize + 100; i++) {
141
141
  Insert(1000+i, 2000+i);
142
142
  ASSERT_EQ(2000+i, Lookup(1000+i));
143
143
  ASSERT_EQ(101, Lookup(100));
144
144
  }
145
145
  ASSERT_EQ(101, Lookup(100));
146
- ASSERT_EQ(2, deleted_keys_.size());
147
- ASSERT_EQ(200, deleted_keys_[0]);
148
- ASSERT_EQ(201, deleted_values_[0]);
146
+ ASSERT_EQ(-1, Lookup(200));
149
147
  }
150
148
 
151
- TEST(CacheTest, HeavyEntry) {
152
- Insert(100, 101);
153
- Insert(200, 201, kCacheSize);
154
- ASSERT_EQ(1, deleted_keys_.size());
155
- ASSERT_EQ(100, deleted_keys_[0]);
156
- ASSERT_EQ(101, deleted_values_[0]);
149
+ TEST(CacheTest, HeavyEntries) {
150
+ // Add a bunch of light and heavy entries and then count the combined
151
+ // size of items still in the cache, which must be approximately the
152
+ // same as the total capacity.
153
+ const int kLight = 1;
154
+ const int kHeavy = 10;
155
+ int added = 0;
156
+ int index = 0;
157
+ while (added < 2*kCacheSize) {
158
+ const int weight = (index & 1) ? kLight : kHeavy;
159
+ Insert(index, 1000+index, weight);
160
+ added += weight;
161
+ index++;
162
+ }
163
+
164
+ int cached_weight = 0;
165
+ for (int i = 0; i < index; i++) {
166
+ const int weight = (i & 1 ? kLight : kHeavy);
167
+ int r = Lookup(i);
168
+ if (r >= 0) {
169
+ cached_weight += weight;
170
+ ASSERT_EQ(1000+i, r);
171
+ }
172
+ }
173
+ ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10);
157
174
  }
158
175
 
159
176
  TEST(CacheTest, NewId) {
@@ -62,10 +62,10 @@ inline uint32_t DecodeFixed32(const char* ptr) {
62
62
  memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load
63
63
  return result;
64
64
  } else {
65
- return ((static_cast<uint32_t>(ptr[0]))
66
- | (static_cast<uint32_t>(ptr[1]) << 8)
67
- | (static_cast<uint32_t>(ptr[2]) << 16)
68
- | (static_cast<uint32_t>(ptr[3]) << 24));
65
+ return ((static_cast<uint32_t>(static_cast<unsigned char>(ptr[0])))
66
+ | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[1])) << 8)
67
+ | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[2])) << 16)
68
+ | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[3])) << 24));
69
69
  }
70
70
  }
71
71
 
@@ -2,6 +2,7 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file. See the AUTHORS file for names of contributors.
4
4
 
5
+ #include <algorithm>
5
6
  #include <stdint.h>
6
7
  #include "leveldb/comparator.h"
7
8
  #include "leveldb/slice.h"
data/leveldb/util/env.cc CHANGED
@@ -18,14 +18,19 @@ RandomAccessFile::~RandomAccessFile() {
18
18
  WritableFile::~WritableFile() {
19
19
  }
20
20
 
21
+ Logger::~Logger() {
22
+ }
23
+
21
24
  FileLock::~FileLock() {
22
25
  }
23
26
 
24
- void Log(Env* env, WritableFile* info_log, const char* format, ...) {
25
- va_list ap;
26
- va_start(ap, format);
27
- env->Logv(info_log, format, ap);
28
- va_end(ap);
27
+ void Log(Logger* info_log, const char* format, ...) {
28
+ if (info_log != NULL) {
29
+ va_list ap;
30
+ va_start(ap, format);
31
+ info_log->Logv(format, ap);
32
+ va_end(ap);
33
+ }
29
34
  }
30
35
 
31
36
  Status WriteStringToFile(Env* env, const Slice& data,
@@ -23,11 +23,16 @@
23
23
  #include "leveldb/slice.h"
24
24
  #include "port/port.h"
25
25
  #include "util/logging.h"
26
+ #include "util/posix_logger.h"
26
27
 
27
28
  namespace leveldb {
28
29
 
29
30
  namespace {
30
31
 
32
+ static Status IOError(const std::string& context, int err_number) {
33
+ return Status::IOError(context, strerror(err_number));
34
+ }
35
+
31
36
  class PosixSequentialFile: public SequentialFile {
32
37
  private:
33
38
  std::string filename_;
@@ -47,7 +52,7 @@ class PosixSequentialFile: public SequentialFile {
47
52
  // We leave status as ok if we hit the end of the file
48
53
  } else {
49
54
  // A partial read with an error: return a non-ok status
50
- s = Status::IOError(filename_, strerror(errno));
55
+ s = IOError(filename_, errno);
51
56
  }
52
57
  }
53
58
  return s;
@@ -55,7 +60,7 @@ class PosixSequentialFile: public SequentialFile {
55
60
 
56
61
  virtual Status Skip(uint64_t n) {
57
62
  if (fseek(file_, n, SEEK_CUR)) {
58
- return Status::IOError(filename_, strerror(errno));
63
+ return IOError(filename_, errno);
59
64
  }
60
65
  return Status::OK();
61
66
  }
@@ -78,7 +83,7 @@ class PosixRandomAccessFile: public RandomAccessFile {
78
83
  *result = Slice(scratch, (r < 0) ? 0 : r);
79
84
  if (r < 0) {
80
85
  // An error: return a non-ok status
81
- s = Status::IOError(filename_, strerror(errno));
86
+ s = IOError(filename_, errno);
82
87
  }
83
88
  return s;
84
89
  }
@@ -114,13 +119,16 @@ class PosixMmapFile : public WritableFile {
114
119
  return s;
115
120
  }
116
121
 
117
- void UnmapCurrentRegion() {
122
+ bool UnmapCurrentRegion() {
123
+ bool result = true;
118
124
  if (base_ != NULL) {
119
125
  if (last_sync_ < limit_) {
120
126
  // Defer syncing this data until next Sync() call, if any
121
127
  pending_sync_ = true;
122
128
  }
123
- munmap(base_, limit_ - base_);
129
+ if (munmap(base_, limit_ - base_) != 0) {
130
+ result = false;
131
+ }
124
132
  file_offset_ += limit_ - base_;
125
133
  base_ = NULL;
126
134
  limit_ = NULL;
@@ -132,6 +140,7 @@ class PosixMmapFile : public WritableFile {
132
140
  map_size_ *= 2;
133
141
  }
134
142
  }
143
+ return result;
135
144
  }
136
145
 
137
146
  bool MapNewRegion() {
@@ -181,8 +190,10 @@ class PosixMmapFile : public WritableFile {
181
190
  assert(dst_ <= limit_);
182
191
  size_t avail = limit_ - dst_;
183
192
  if (avail == 0) {
184
- UnmapCurrentRegion();
185
- MapNewRegion();
193
+ if (!UnmapCurrentRegion() ||
194
+ !MapNewRegion()) {
195
+ return IOError(filename_, errno);
196
+ }
186
197
  }
187
198
 
188
199
  size_t n = (left <= avail) ? left : avail;
@@ -197,17 +208,18 @@ class PosixMmapFile : public WritableFile {
197
208
  virtual Status Close() {
198
209
  Status s;
199
210
  size_t unused = limit_ - dst_;
200
- UnmapCurrentRegion();
201
- if (unused > 0) {
211
+ if (!UnmapCurrentRegion()) {
212
+ s = IOError(filename_, errno);
213
+ } else if (unused > 0) {
202
214
  // Trim the extra space at the end of the file
203
215
  if (ftruncate(fd_, file_offset_ - unused) < 0) {
204
- s = Status::IOError(filename_, strerror(errno));
216
+ s = IOError(filename_, errno);
205
217
  }
206
218
  }
207
219
 
208
220
  if (close(fd_) < 0) {
209
221
  if (s.ok()) {
210
- s = Status::IOError(filename_, strerror(errno));
222
+ s = IOError(filename_, errno);
211
223
  }
212
224
  }
213
225
 
@@ -228,7 +240,7 @@ class PosixMmapFile : public WritableFile {
228
240
  // Some unmapped data was not synced
229
241
  pending_sync_ = false;
230
242
  if (fdatasync(fd_) < 0) {
231
- s = Status::IOError(filename_, strerror(errno));
243
+ s = IOError(filename_, errno);
232
244
  }
233
245
  }
234
246
 
@@ -239,7 +251,7 @@ class PosixMmapFile : public WritableFile {
239
251
  size_t p2 = TruncateToPageBoundary(dst_ - base_ - 1);
240
252
  last_sync_ = dst_;
241
253
  if (msync(base_ + p1, p2 - p1 + page_size_, MS_SYNC) < 0) {
242
- s = Status::IOError(filename_, strerror(errno));
254
+ s = IOError(filename_, errno);
243
255
  }
244
256
  }
245
257
 
@@ -276,7 +288,7 @@ class PosixEnv : public Env {
276
288
  FILE* f = fopen(fname.c_str(), "r");
277
289
  if (f == NULL) {
278
290
  *result = NULL;
279
- return Status::IOError(fname, strerror(errno));
291
+ return IOError(fname, errno);
280
292
  } else {
281
293
  *result = new PosixSequentialFile(fname, f);
282
294
  return Status::OK();
@@ -288,7 +300,7 @@ class PosixEnv : public Env {
288
300
  int fd = open(fname.c_str(), O_RDONLY);
289
301
  if (fd < 0) {
290
302
  *result = NULL;
291
- return Status::IOError(fname, strerror(errno));
303
+ return IOError(fname, errno);
292
304
  }
293
305
  *result = new PosixRandomAccessFile(fname, fd);
294
306
  return Status::OK();
@@ -300,7 +312,7 @@ class PosixEnv : public Env {
300
312
  const int fd = open(fname.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644);
301
313
  if (fd < 0) {
302
314
  *result = NULL;
303
- s = Status::IOError(fname, strerror(errno));
315
+ s = IOError(fname, errno);
304
316
  } else {
305
317
  *result = new PosixMmapFile(fname, fd, page_size_);
306
318
  }
@@ -316,7 +328,7 @@ class PosixEnv : public Env {
316
328
  result->clear();
317
329
  DIR* d = opendir(dir.c_str());
318
330
  if (d == NULL) {
319
- return Status::IOError(dir, strerror(errno));
331
+ return IOError(dir, errno);
320
332
  }
321
333
  struct dirent* entry;
322
334
  while ((entry = readdir(d)) != NULL) {
@@ -329,7 +341,7 @@ class PosixEnv : public Env {
329
341
  virtual Status DeleteFile(const std::string& fname) {
330
342
  Status result;
331
343
  if (unlink(fname.c_str()) != 0) {
332
- result = Status::IOError(fname, strerror(errno));
344
+ result = IOError(fname, errno);
333
345
  }
334
346
  return result;
335
347
  };
@@ -337,7 +349,7 @@ class PosixEnv : public Env {
337
349
  virtual Status CreateDir(const std::string& name) {
338
350
  Status result;
339
351
  if (mkdir(name.c_str(), 0755) != 0) {
340
- result = Status::IOError(name, strerror(errno));
352
+ result = IOError(name, errno);
341
353
  }
342
354
  return result;
343
355
  };
@@ -345,7 +357,7 @@ class PosixEnv : public Env {
345
357
  virtual Status DeleteDir(const std::string& name) {
346
358
  Status result;
347
359
  if (rmdir(name.c_str()) != 0) {
348
- result = Status::IOError(name, strerror(errno));
360
+ result = IOError(name, errno);
349
361
  }
350
362
  return result;
351
363
  };
@@ -355,7 +367,7 @@ class PosixEnv : public Env {
355
367
  struct stat sbuf;
356
368
  if (stat(fname.c_str(), &sbuf) != 0) {
357
369
  *size = 0;
358
- s = Status::IOError(fname, strerror(errno));
370
+ s = IOError(fname, errno);
359
371
  } else {
360
372
  *size = sbuf.st_size;
361
373
  }
@@ -365,7 +377,7 @@ class PosixEnv : public Env {
365
377
  virtual Status RenameFile(const std::string& src, const std::string& target) {
366
378
  Status result;
367
379
  if (rename(src.c_str(), target.c_str()) != 0) {
368
- result = Status::IOError(src, strerror(errno));
380
+ result = IOError(src, errno);
369
381
  }
370
382
  return result;
371
383
  }
@@ -375,9 +387,9 @@ class PosixEnv : public Env {
375
387
  Status result;
376
388
  int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644);
377
389
  if (fd < 0) {
378
- result = Status::IOError(fname, strerror(errno));
390
+ result = IOError(fname, errno);
379
391
  } else if (LockOrUnlock(fd, true) == -1) {
380
- result = Status::IOError("lock " + fname, strerror(errno));
392
+ result = IOError("lock " + fname, errno);
381
393
  close(fd);
382
394
  } else {
383
395
  PosixFileLock* my_lock = new PosixFileLock;
@@ -391,7 +403,7 @@ class PosixEnv : public Env {
391
403
  PosixFileLock* my_lock = reinterpret_cast<PosixFileLock*>(lock);
392
404
  Status result;
393
405
  if (LockOrUnlock(my_lock->fd_, false) == -1) {
394
- result = Status::IOError(strerror(errno));
406
+ result = IOError("unlock", errno);
395
407
  }
396
408
  close(my_lock->fd_);
397
409
  delete my_lock;
@@ -416,72 +428,21 @@ class PosixEnv : public Env {
416
428
  return Status::OK();
417
429
  }
418
430
 
419
- virtual void Logv(WritableFile* info_log, const char* format, va_list ap) {
431
+ static uint64_t gettid() {
420
432
  pthread_t tid = pthread_self();
421
433
  uint64_t thread_id = 0;
422
434
  memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid)));
435
+ return thread_id;
436
+ }
423
437
 
424
- // We try twice: the first time with a fixed-size stack allocated buffer,
425
- // and the second time with a much larger dynamically allocated buffer.
426
- char buffer[500];
427
- for (int iter = 0; iter < 2; iter++) {
428
- char* base;
429
- int bufsize;
430
- if (iter == 0) {
431
- bufsize = sizeof(buffer);
432
- base = buffer;
433
- } else {
434
- bufsize = 30000;
435
- base = new char[bufsize];
436
- }
437
- char* p = base;
438
- char* limit = base + bufsize;
439
-
440
- struct timeval now_tv;
441
- gettimeofday(&now_tv, NULL);
442
- const time_t seconds = now_tv.tv_sec;
443
- struct tm t;
444
- localtime_r(&seconds, &t);
445
- p += snprintf(p, limit - p,
446
- "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
447
- t.tm_year + 1900,
448
- t.tm_mon + 1,
449
- t.tm_mday,
450
- t.tm_hour,
451
- t.tm_min,
452
- t.tm_sec,
453
- static_cast<int>(now_tv.tv_usec),
454
- static_cast<long long unsigned int>(thread_id));
455
-
456
- // Print the message
457
- if (p < limit) {
458
- va_list backup_ap;
459
- va_copy(backup_ap, ap);
460
- p += vsnprintf(p, limit - p, format, backup_ap);
461
- va_end(backup_ap);
462
- }
463
-
464
- // Truncate to available space if necessary
465
- if (p >= limit) {
466
- if (iter == 0) {
467
- continue; // Try again with larger buffer
468
- } else {
469
- p = limit - 1;
470
- }
471
- }
472
-
473
- // Add newline if necessary
474
- if (p == base || p[-1] != '\n') {
475
- *p++ = '\n';
476
- }
477
-
478
- assert(p <= limit);
479
- info_log->Append(Slice(base, p - base));
480
- info_log->Flush();
481
- if (base != buffer) {
482
- delete[] base;
483
- }
484
- break;
438
+ virtual Status NewLogger(const std::string& fname, Logger** result) {
439
+ FILE* f = fopen(fname.c_str(), "w");
440
+ if (f == NULL) {
441
+ *result = NULL;
442
+ return IOError(fname, errno);
443
+ } else {
444
+ *result = new PosixLogger(f, &PosixEnv::gettid);
445
+ return Status::OK();
485
446
  }
486
447
  }
487
448
 
@@ -55,6 +55,17 @@ void Histogram::Add(double value) {
55
55
  sum_squares_ += (value * value);
56
56
  }
57
57
 
58
+ void Histogram::Merge(const Histogram& other) {
59
+ if (other.min_ < min_) min_ = other.min_;
60
+ if (other.max_ > max_) max_ = other.max_;
61
+ num_ += other.num_;
62
+ sum_ += other.sum_;
63
+ sum_squares_ += other.sum_squares_;
64
+ for (int b = 0; b < kNumBuckets; b++) {
65
+ buckets_[b] += other.buckets_[b];
66
+ }
67
+ }
68
+
58
69
  double Histogram::Median() const {
59
70
  return Percentile(50.0);
60
71
  }
@@ -16,6 +16,7 @@ class Histogram {
16
16
 
17
17
  void Clear();
18
18
  void Add(double value);
19
+ void Merge(const Histogram& other);
19
20
 
20
21
  std::string ToString() const;
21
22
 
@@ -0,0 +1,98 @@
1
+ // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file. See the AUTHORS file for names of contributors.
4
+ //
5
+ // Logger implementation that can be shared by all environments
6
+ // where enough posix functionality is available.
7
+
8
+ #ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
9
+ #define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
10
+
11
+ #include <algorithm>
12
+ #include <stdio.h>
13
+ #include <sys/time.h>
14
+ #include <time.h>
15
+ #include "leveldb/env.h"
16
+
17
+ namespace leveldb {
18
+
19
+ class PosixLogger : public Logger {
20
+ private:
21
+ FILE* file_;
22
+ uint64_t (*gettid_)(); // Return the thread id for the current thread
23
+ public:
24
+ PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { }
25
+ virtual ~PosixLogger() {
26
+ fclose(file_);
27
+ }
28
+ virtual void Logv(const char* format, va_list ap) {
29
+ const uint64_t thread_id = (*gettid_)();
30
+
31
+ // We try twice: the first time with a fixed-size stack allocated buffer,
32
+ // and the second time with a much larger dynamically allocated buffer.
33
+ char buffer[500];
34
+ for (int iter = 0; iter < 2; iter++) {
35
+ char* base;
36
+ int bufsize;
37
+ if (iter == 0) {
38
+ bufsize = sizeof(buffer);
39
+ base = buffer;
40
+ } else {
41
+ bufsize = 30000;
42
+ base = new char[bufsize];
43
+ }
44
+ char* p = base;
45
+ char* limit = base + bufsize;
46
+
47
+ struct timeval now_tv;
48
+ gettimeofday(&now_tv, NULL);
49
+ const time_t seconds = now_tv.tv_sec;
50
+ struct tm t;
51
+ localtime_r(&seconds, &t);
52
+ p += snprintf(p, limit - p,
53
+ "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",
54
+ t.tm_year + 1900,
55
+ t.tm_mon + 1,
56
+ t.tm_mday,
57
+ t.tm_hour,
58
+ t.tm_min,
59
+ t.tm_sec,
60
+ static_cast<int>(now_tv.tv_usec),
61
+ static_cast<long long unsigned int>(thread_id));
62
+
63
+ // Print the message
64
+ if (p < limit) {
65
+ va_list backup_ap;
66
+ va_copy(backup_ap, ap);
67
+ p += vsnprintf(p, limit - p, format, backup_ap);
68
+ va_end(backup_ap);
69
+ }
70
+
71
+ // Truncate to available space if necessary
72
+ if (p >= limit) {
73
+ if (iter == 0) {
74
+ continue; // Try again with larger buffer
75
+ } else {
76
+ p = limit - 1;
77
+ }
78
+ }
79
+
80
+ // Add newline if necessary
81
+ if (p == base || p[-1] != '\n') {
82
+ *p++ = '\n';
83
+ }
84
+
85
+ assert(p <= limit);
86
+ fwrite(base, 1, p - base, file_);
87
+ fflush(file_);
88
+ if (base != buffer) {
89
+ delete[] base;
90
+ }
91
+ break;
92
+ }
93
+ }
94
+ };
95
+
96
+ }
97
+
98
+ #endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_