| 1 | //===-- lib/runtime/unit-map.h ----------------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | // Maps Fortran unit numbers to their ExternalFileUnit instances. |
| 10 | // A simple hash table with forward-linked chains per bucket. |
| 11 | |
| 12 | #ifndef FLANG_RT_RUNTIME_UNIT_MAP_H_ |
| 13 | #define FLANG_RT_RUNTIME_UNIT_MAP_H_ |
| 14 | |
| 15 | #include "unit.h" |
| 16 | #include "flang-rt/runtime/lock.h" |
| 17 | #include "flang-rt/runtime/memory.h" |
| 18 | #include "flang/Common/fast-int-set.h" |
| 19 | #include <cstdint> |
| 20 | #include <cstdlib> |
| 21 | |
| 22 | namespace Fortran::runtime::io { |
| 23 | |
| 24 | class UnitMap { |
| 25 | public: |
| 26 | ExternalFileUnit *LookUp(int n) { |
| 27 | CriticalSection critical{lock_}; |
| 28 | return Find(n); |
| 29 | } |
| 30 | |
| 31 | ExternalFileUnit *LookUpOrCreate( |
| 32 | int n, const Terminator &terminator, bool &wasExtant) { |
| 33 | CriticalSection critical{lock_}; |
| 34 | if (auto *p{Find(n)}) { |
| 35 | wasExtant = true; |
| 36 | return p; |
| 37 | } else { |
| 38 | wasExtant = false; |
| 39 | return n >= 0 ? &Create(n, terminator) : nullptr; |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | // Unit look-up by name is needed for INQUIRE(FILE="...") |
| 44 | ExternalFileUnit *LookUp(const char *path, std::size_t pathLen) { |
| 45 | CriticalSection critical{lock_}; |
| 46 | return Find(path, pathLen); |
| 47 | } |
| 48 | |
| 49 | ExternalFileUnit &NewUnit(const Terminator &); |
| 50 | |
| 51 | // To prevent races, the unit is removed from the map if it exists, |
| 52 | // and put on the closing_ list until DestroyClosed() is called. |
| 53 | ExternalFileUnit *LookUpForClose(int); |
| 54 | |
| 55 | void DestroyClosed(ExternalFileUnit &); |
| 56 | void CloseAll(IoErrorHandler &); |
| 57 | void FlushAll(IoErrorHandler &); |
| 58 | |
| 59 | private: |
| 60 | struct Chain { |
| 61 | explicit Chain(int n) : unit{n} {} |
| 62 | ExternalFileUnit unit; |
| 63 | OwningPtr<Chain> next{nullptr}; |
| 64 | }; |
| 65 | |
| 66 | static constexpr int buckets_{1031}; // must be prime |
| 67 | |
| 68 | // The pool of recyclable new unit numbers uses the range that |
| 69 | // works even with INTEGER(kind=1). 0 and -1 are never used. |
| 70 | static constexpr int maxNewUnits_{129}; // [ -128 .. 0 ] |
| 71 | |
| 72 | int Hash(int n) { return std::abs(x: n) % buckets_; } |
| 73 | |
| 74 | void Initialize(); |
| 75 | |
| 76 | ExternalFileUnit *Find(int n) { |
| 77 | Chain *previous{nullptr}; |
| 78 | int hash{Hash(n)}; |
| 79 | for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) { |
| 80 | if (p->unit.unitNumber() == n) { |
| 81 | if (previous) { |
| 82 | // Move found unit to front of chain for quicker lookup next time |
| 83 | previous->next.swap(p->next); // now p->next.get() == p |
| 84 | bucket_[hash].swap(p->next); // now bucket_[hash].get() == p |
| 85 | } |
| 86 | return &p->unit; |
| 87 | } |
| 88 | } |
| 89 | return nullptr; |
| 90 | } |
| 91 | ExternalFileUnit *Find(const char *path, std::size_t pathLen); |
| 92 | |
| 93 | ExternalFileUnit &Create(int, const Terminator &); |
| 94 | |
| 95 | Lock lock_; |
| 96 | bool isInitialized_{false}; |
| 97 | OwningPtr<Chain> bucket_[buckets_]{}; // all owned by *this |
| 98 | OwningPtr<Chain> closing_{nullptr}; // units during CLOSE statement |
| 99 | common::FastIntSet<maxNewUnits_> freeNewUnits_; |
| 100 | int emergencyNewUnit_{maxNewUnits_}; // not recycled |
| 101 | }; |
| 102 | } // namespace Fortran::runtime::io |
| 103 | #endif // FLANG_RT_RUNTIME_UNIT_MAP_H_ |
| 104 | |