#ifndef FORTRAN_RUNTIME_UNIT_MAP_H_
#define FORTRAN_RUNTIME_UNIT_MAP_H_
#include "lock.h"
#include "unit.h"
#include "flang/Common/fast-int-set.h"
#include "flang/Runtime/memory.h"
#include <cstdint>
#include <cstdlib>
namespace Fortran::runtime::io {
class UnitMap {
public:
ExternalFileUnit *LookUp(int n) {
CriticalSection critical{lock_};
return Find(n);
}
ExternalFileUnit *LookUpOrCreate(
int n, const Terminator &terminator, bool &wasExtant) {
CriticalSection critical{lock_};
if (auto *p{Find(n)}) {
wasExtant = true;
return p;
} else {
wasExtant = false;
return n >= 0 ? &Create(n, terminator) : nullptr;
}
}
ExternalFileUnit *LookUp(const char *path, std::size_t pathLen) {
CriticalSection critical{lock_};
return Find(path, pathLen);
}
ExternalFileUnit &NewUnit(const Terminator &);
ExternalFileUnit *LookUpForClose(int);
void DestroyClosed(ExternalFileUnit &);
void CloseAll(IoErrorHandler &);
void FlushAll(IoErrorHandler &);
private:
struct Chain {
explicit Chain(int n) : unit{n} {}
ExternalFileUnit unit;
OwningPtr<Chain> next{nullptr};
};
static constexpr int buckets_{1031};
static constexpr int maxNewUnits_{129};
int Hash(int n) { return std::abs(n) % buckets_; }
void Initialize();
ExternalFileUnit *Find(int n) {
Chain *previous{nullptr};
int hash{Hash(n)};
for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) {
if (p->unit.unitNumber() == n) {
if (previous) {
previous->next.swap(p->next);
bucket_[hash].swap(p->next);
}
return &p->unit;
}
}
return nullptr;
}
ExternalFileUnit *Find(const char *path, std::size_t pathLen);
ExternalFileUnit &Create(int, const Terminator &);
Lock lock_;
bool isInitialized_{false};
OwningPtr<Chain> bucket_[buckets_]{};
OwningPtr<Chain> closing_{nullptr};
common::FastIntSet<maxNewUnits_> freeNewUnits_;
int emergencyNewUnit_{maxNewUnits_};
};
}
#endif