<commit_before>#pragma once

#include "kernel.hh"
#include "refcache.hh"
#include "chainhash.hh"
#include "radix_array.hh"
#include "page_info.hh"
#include "kalloc.hh"
#include "fs.h"

class mdir;
class mfile;
class mdev;
class msock;

class mnode : public refcache::weak_referenced
{
public:
  struct types {
    enum {
      dir = 1,
      file,
      dev,
      sock,
    };
  };

  static sref<mnode> get(u64 n);
  static sref<mnode> alloc(u8 type);

  void cache_pin(bool flag);
  u8 type() const;

  mdir* as_dir();
  const mdir* as_dir() const;
  mfile* as_file();
  const mfile* as_file() const;
  mdev* as_dev();
  const mdev* as_dev() const;
  msock* as_sock();
  const msock* as_sock() const;

  class linkcount : public refcache::referenced {
  public:
    linkcount() : refcache::referenced(0) {};
    void onzero() override;
  };

  const u64 inum_;
  linkcount nlink_;

protected:
  mnode(u64 inum);

private:
  void onzero() override;

  std::atomic<bool> cache_pin_;
  std::atomic<bool> dirty_;
  std::atomic<bool> valid_;
};

/*
 * An mlinkref represents a link count reference on an mnode.
 * The caller must ensure that mlinkref::acquire() is not called
 * after mnode::nlink_ reaches stable zero, perhaps by blocking
 * refcache epochs using cli when looking up the inode number in
 * a directory.
 *
 * Each mlinkref holds a reference to the mnode as well, to ensure
 * that the memory used to store the nlink_ count is not evicted
 * before all of the refcache deltas are flushed.  Otherwise this
 * would just be an sref<linkcount>.
 */
class mlinkref {
public:
  mlinkref() {}
  mlinkref(sref<mnode> mref) : m_(mref) {}

  sref<mnode> mn() {
    return m_;
  }

  bool held() {
    return !!l_;
  }

  /*
   * Increment the link count on the mnode.
   */
  void acquire() {
    assert(m_ && !l_);
    l_ = sref<mnode::linkcount>::newref(&m_->nlink_);
  }

  /*
   * Transfer an existing link count on the mnode to this mlinkref.
   */
  void transfer() {
    assert(m_ && !l_);
    l_ = sref<mnode::linkcount>::transfer(&m_->nlink_);
  }

private:
  /*
   * The order is important due to C++ constructor/destructor
   * rules: we must hold the mnode reference while manipulating
   * the linkcount reference.
   */
  sref<mnode> m_;
  sref<mnode::linkcount> l_;
};


class mdir : public mnode {
private:
  mdir(u64 inum) : mnode(inum), map_(257) {}
  NEW_DELETE_OPS(mdir);
  friend sref<mnode> mnode::get(u64);

  chainhash<strbuf<DIRSIZ>, u64> map_;

public:
  bool insert(const strbuf<DIRSIZ>& name, mlinkref* ilink) {
    if (name == ".")
      return false;
    if (!map_.insert(name, ilink->mn()->inum_))
      return false;
    assert(ilink->held());
    ilink->mn()->nlink_.inc();
    return true;
  }

  bool remove(const strbuf<DIRSIZ>& name, sref<mnode> m) {
    if (!map_.remove(name, m->inum_))
      return false;
    m->nlink_.dec();
    return true;
  }

  bool replace(const strbuf<DIRSIZ>& name, sref<mnode> mold, mlinkref* ilinknew) {
    if (!map_.replace(name, mold->inum_, ilinknew->mn()->inum_))
      return false;
    assert(ilinknew->held());
    ilinknew->mn()->nlink_.inc();
    mold->nlink_.dec();
    return true;
  }

  sref<mnode> lookup(const strbuf<DIRSIZ>& name) const {
    if (name == ".")
      return mnode::get(inum_);

    u64 iprev = -1;
    for (;;) {
      u64 inum;
      if (!map_.lookup(name, &inum))
        return sref<mnode>();

      sref<mnode> m = mnode::get(inum);
      if (m)
        return m;

      /*<commit_msg>make rename() slightly more scalable<commit_after>#pragma once

#include "kernel.hh"
#include "refcache.hh"
#include "chainhash.hh"
#include "radix_array.hh"
#include "page_info.hh"
#include "kalloc.hh"
#include "fs.h"

class mdir;
class mfile;
class mdev;
class msock;

class mnode : public refcache::weak_referenced
{
public:
  struct types {
    enum {
      dir = 1,
      file,
      dev,
      sock,
    };
  };

  static sref<mnode> get(u64 n);
  static sref<mnode> alloc(u8 type);

  void cache_pin(bool flag);
  u8 type() const;

  mdir* as_dir();
  const mdir* as_dir() const;
  mfile* as_file();
  const mfile* as_file() const;
  mdev* as_dev();
  const mdev* as_dev() const;
  msock* as_sock();
  const msock* as_sock() const;

  class linkcount : public refcache::referenced {
  public:
    linkcount() : refcache::referenced(0) {};
    void onzero() override;
  };

  const u64 inum_;
  linkcount nlink_;

protected:
  mnode(u64 inum);

private:
  void onzero() override;

  std::atomic<bool> cache_pin_;
  std::atomic<bool> dirty_;
  std::atomic<bool> valid_;
};

/*
 * An mlinkref represents a link count reference on an mnode.
 * The caller must ensure that mlinkref::acquire() is not called
 * after mnode::nlink_ reaches stable zero, perhaps by blocking
 * refcache epochs using cli when looking up the inode number in
 * a directory.
 *
 * Each mlinkref holds a reference to the mnode as well, to ensure
 * that the memory used to store the nlink_ count is not evicted
 * before all of the refcache deltas are flushed.  Otherwise this
 * would just be an sref<linkcount>.
 */
class mlinkref {
public:
  mlinkref() {}
  mlinkref(sref<mnode> mref) : m_(mref) {}

  sref<mnode> mn() {
    return m_;
  }

  bool held() {
    return !!l_;
  }

  /*
   * Increment the link count on the mnode.
   */
  void acquire() {
    assert(m_ && !l_);
    l_ = sref<mnode::linkcount>::newref(&m_->nlink_);
  }

  /*
   * Transfer an existing link count on the mnode to this mlinkref.
   */
  void transfer() {
    assert(m_ && !l_);
    l_ = sref<mnode::linkcount>::transfer(&m_->nlink_);
  }

private:
  /*
   * The order is important due to C++ constructor/destructor
   * rules: we must hold the mnode reference while manipulating
   * the linkcount reference.
   */
  sref<mnode> m_;
  sref<mnode::linkcount> l_;
};


class mdir : public mnode {
private:
  mdir(u64 inum) : mnode(inum), map_(257) {}
  NEW_DELETE_OPS(mdir);
  friend sref<mnode> mnode::get(u64);

  chainhash<strbuf<DIRSIZ>, u64> map_;

public:
  bool insert(const strbuf<DIRSIZ>& name, mlinkref* ilink) {
    if (name == ".")
      return false;
    if (!map_.insert(name, ilink->mn()->inum_))
      return false;
    assert(ilink->held());
    ilink->mn()->nlink_.inc();
    return true;
  }

  bool remove(const strbuf<DIRSIZ>& name, sref<mnode> m) {
    if (!map_.remove(name, m->inum_))
      return false;
    m->nlink_.dec();
    return true;
  }

  bool replace(const strbuf<DIRSIZ>& name, sref<mnode> mold, mlinkref* ilinknew) {
    if (mold->inum_ == ilinknew->mn()->inum_)
      return true;
    if (!map_.replace(name, mold->inum_, ilinknew->mn()->inum_))
      return false;
    assert(ilinknew->held());
    ilinknew->mn()->nlink_.inc();
    mold->nlink_.dec();
    return true;
  }

  sref<mnode> lookup(const strbuf<DIRSIZ>& name) const {
    if (name == ".")
      return mnode::get(inum_);

    u64 iprev = -1;
    for (;;) {
      u64 inum;
      if (!map_.lookup(name, &inum))
        return sref<mnode>();

      sref<mnode> m = mnode::get(inum);
      if (m)
        return m;

      /*
{'fraction_non_alphanumeric': 0.14311952528962985, 'fraction_numerical': 0.006781576716586606, 'mean_word_length': 3.1641176470588235, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 86, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1726872', 'n_tokens_mistral': 2631, 'n_tokens_neox': 2562, 'n_words': 705}