leveldb-ruby 0.7 → 0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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_