//===-- runtime/unit-map.h --------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // Maps Fortran unit numbers to their ExternalFileUnit instances. // A simple hash table with forward-linked chains per bucket. #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 #include 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; } } // Unit look-up by name is needed for INQUIRE(FILE="...") ExternalFileUnit *LookUp(const char *path, std::size_t pathLen) { CriticalSection critical{lock_}; return Find(path, pathLen); } ExternalFileUnit &NewUnit(const Terminator &); // To prevent races, the unit is removed from the map if it exists, // and put on the closing_ list until DestroyClosed() is called. ExternalFileUnit *LookUpForClose(int); void DestroyClosed(ExternalFileUnit &); void CloseAll(IoErrorHandler &); void FlushAll(IoErrorHandler &); private: struct Chain { explicit Chain(int n) : unit{n} {} ExternalFileUnit unit; OwningPtr next{nullptr}; }; static constexpr int buckets_{1031}; // must be prime // The pool of recyclable new unit numbers uses the range that // works even with INTEGER(kind=1). 0 and -1 are never used. static constexpr int maxNewUnits_{129}; // [ -128 .. 0 ] 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) { // Move found unit to front of chain for quicker lookup next time previous->next.swap(p->next); // now p->next.get() == p bucket_[hash].swap(p->next); // now bucket_[hash].get() == p } 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 bucket_[buckets_]{}; // all owned by *this OwningPtr closing_{nullptr}; // units during CLOSE statement common::FastIntSet freeNewUnits_; int emergencyNewUnit_{maxNewUnits_}; // not recycled }; } // namespace Fortran::runtime::io #endif // FORTRAN_RUNTIME_UNIT_MAP_H_