From 8d1dd7a3dc8e882630f0f6d9be0192f70aa824b1 Mon Sep 17 00:00:00 2001
From: Avi Kivity <avi.kivity@gmail.com>
Date: Mon, 31 Dec 2012 16:21:35 +0200
Subject: [PATCH] fs: directory entry cache

Store directory entries in a hash table for future lookup.
---
 fs/bootfs.cc |  2 +-
 fs/bootfs.hh |  2 +-
 fs/fs.cc     | 25 +++++++++++++++++++++++++
 fs/fs.hh     | 14 +++++++++++++-
 4 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/fs/bootfs.cc b/fs/bootfs.cc
index 1b563fc44..8159f8d26 100644
--- a/fs/bootfs.cc
+++ b/fs/bootfs.cc
@@ -53,7 +53,7 @@ bootfs::dir::dir(bootfs& fs, std::string path)
 {
 }
 
-fileref bootfs::dir::open(std::string name)
+fileref bootfs::dir::do_open(std::string name)
 {
     return _fs.do_open(_path + name);
 }
diff --git a/fs/bootfs.hh b/fs/bootfs.hh
index a3d332eb7..63f29cdc8 100644
--- a/fs/bootfs.hh
+++ b/fs/bootfs.hh
@@ -33,7 +33,7 @@ private:
 class bootfs::dir : public ::dir {
 public:
     dir(bootfs& fs, std::string path);
-    virtual fileref open(std::string name);
+    virtual fileref do_open(std::string name);
     virtual uint64_t size();
     virtual void read(void* buffer, uint64_t offset, uint64_t len);
 private:
diff --git a/fs/fs.cc b/fs/fs.cc
index 68c2af78c..2dab7d451 100644
--- a/fs/fs.cc
+++ b/fs/fs.cc
@@ -2,6 +2,18 @@
 
 filesystem* rootfs;
 
+namespace std {
+
+template <>
+struct hash<file::cache_key> {
+    size_t operator()(file::cache_key k) const {
+        return reinterpret_cast<uintptr_t>(k.first.get())
+               ^ std::hash<std::string>()(k.second);
+    }
+};
+
+}
+
 file::file()
     : _refs(0)
 {
@@ -43,8 +55,21 @@ fileref filesystem::open(std::string name)
     return d->open(name.substr(s, name.npos));
 }
 
+fileref dir::open(std::string name)
+{
+    cache_key key(this, name);
+    auto ret = _cache.find(key);
+    if (ret == _cache.end()) {
+        auto f = do_open(name);
+        ret = _cache.insert(cache_type::value_type(key, f)).first;
+    }
+    return ret->second;
+}
+
 dirref dir::subdir(std::string name)
 {
     // trivial implementation, can be overridden
     return boost::dynamic_pointer_cast<dir>(open(name));
 }
+
+file::cache_type file::_cache;
diff --git a/fs/fs.hh b/fs/fs.hh
index 85d1ccc77..570f35ac9 100644
--- a/fs/fs.hh
+++ b/fs/fs.hh
@@ -4,6 +4,7 @@
 #include <string>
 #include <cstdint>
 #include <boost/intrusive_ptr.hpp>
+#include <unordered_map>
 
 class file;
 class dir;
@@ -24,12 +25,23 @@ private:
     unsigned _refs; // FIXME: make atomic
     friend void intrusive_ptr_add_ref(file* f) { f->ref(); }
     friend void intrusive_ptr_release(file* f) { f->unref(); }
+    friend class dir;
+private:
+    typedef std::pair<dirref, std::string> cache_key;
+    // FIXME: an intrusive container
+    typedef std::unordered_map<cache_key, fileref> cache_type;
+    static cache_type _cache;
+    friend struct std::hash<cache_key>;
 };
 
 class dir : public file {
 public:
-    virtual fileref open(std::string name) = 0;
+    fileref open(std::string name);
+    virtual fileref do_open(std::string name) = 0;
     dirref subdir(std::string name);
+private:
+    dirref _parent;
+    std::string _name;
 };
 
 class filesystem {
-- 
GitLab