1 | //===-- 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 FORTRAN_RUNTIME_UNIT_MAP_H_ |
13 | #define FORTRAN_RUNTIME_UNIT_MAP_H_ |
14 | |
15 | #include "lock.h" |
16 | #include "unit.h" |
17 | #include "flang/Common/fast-int-set.h" |
18 | #include "flang/Runtime/memory.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 // FORTRAN_RUNTIME_UNIT_MAP_H_ |
104 | |