text: // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file. See the AUTHORS file for names of contributors.
  
  #include "db/table_cache.h"
  
  #include "db/filename.h"
  #include "leveldb/env.h"
  #include "leveldb/table.h"
  #include "util/coding.h"
  
  namespace leveldb {
  
  struct TableAndFile {
    RandomAccessFile* file;
    Table* table;
  };
  
  static void DeleteEntry(const Slice& key, void* value) {
    TableAndFile* tf = reinterpret_cast<TableAndFile*>(value);
    delete tf->table;
    delete tf->file;
    delete tf;
  }
  
  static void UnrefEntry(void* arg1, void* arg2) {
    Cache* cache = reinterpret_cast<Cache*>(arg1);
    Cache::Handle* h = reinterpret_cast<Cache::Handle*>(arg2);
    cache->Release(h);
  }
  
  TableCache::TableCache(const std::string& dbname,
                         const Options* options,
                         int entries)
      : env_(options->env),
        dbname_(dbname),
        options_(options),
        cache_(NewLRUCache(entries)) {
  }
  
  TableCache::~TableCache() {
    delete cache_;
  }
  
  Status TableCache::FindTable(uint64_t file_number, uint64_t file_size,
                               Cache::Handle** handle) {
    Status s;
    char buf[sizeof(file_number)];
    EncodeFixed64(buf, file_number);
    Slice key(buf, sizeof(buf));
    *handle = cache_->Lookup(key);
    if (*handle == NULL) {
      std::string fname = TableFileName(dbname_, file_number);
      RandomAccessFile* file = NULL;
      Table* table = NULL;
      s = env_->NewRandomAccessFile(fname, &file);
      if (s.ok()) {
        s = Table::Open(*options_, file, file_size, &table);
      }
  
      if (!s.ok()) {
        assert(table == NULL);
        delete file;
        // We do not cache error results so that if the error is transient,
        // or somebody repairs the file, we recover automatically.
      } else {
        TableAndFile* tf = new TableAndFile;
        tf->file = file;
        tf->table = table;
        *handle = cache_->Insert(key, tf, 1, &DeleteEntry);
      }
    }
    return s;
  }
  
  Iterator* TableCache::NewIterator(const ReadOptions& options,
                                    uint64_t file_number,
                                    uint64_t file_size,
                                    Table** tableptr) {
    if (tableptr != NULL) {
      *tableptr = NULL;
    }
  
    Cache::Handle* handle = NULL;
    Status s = FindTable(file_number, file_size, &handle);
    if (!s.ok()) {
      return NewErrorIterator(s);
    }
  
    Table* table = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
    Iterator* result = table->NewIterator(options);
    result->RegisterCleanup(&UnrefEntry, cache_, handle);
    if (tableptr != NULL) {
      *tableptr = table;
    }
    return result;
  }
  
  Status TableCache::Get(const ReadOptions& options,
                         uint64_t file_number,
                         uint64_t file_size,
                         const Slice& k,
                         void* arg,
                         void (*saver)(void*, const Slice&, const Slice&),
                         bool& filtered) {
    Cache::Handle* handle = NULL;
    Status s = FindTable(file_number, file_size, &handle);
    if (s.ok()) {
      Table* t = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
      s = t->InternalGet(options, k, arg, saver, filtered);
      cache_->Release(handle);
    }
    return s;
  }
  
  void TableCache::Evict(uint64_t file_number) {
    char buf[sizeof(file_number)];
    EncodeFixed64(buf, file_number);
    cache_->Erase(Slice(buf, sizeof(buf)));
  }
  
  }  // namespace leveldb
  