| //===-- 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 <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; | 
 |     } | 
 |   } | 
 |  | 
 |   // 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<Chain> 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<Chain> bucket_[buckets_]{}; // all owned by *this | 
 |   OwningPtr<Chain> closing_{nullptr}; // units during CLOSE statement | 
 |   common::FastIntSet<maxNewUnits_> freeNewUnits_; | 
 |   int emergencyNewUnit_{maxNewUnits_}; // not recycled | 
 | }; | 
 | } // namespace Fortran::runtime::io | 
 | #endif // FORTRAN_RUNTIME_UNIT_MAP_H_ |