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 "memory.h" 17 #include "unit.h" 18 #include <cstdlib> 19 20 namespace Fortran::runtime::io { 21 22 class UnitMap { 23 public: LookUp(int n)24 ExternalFileUnit *LookUp(int n) { 25 CriticalSection critical{lock_}; 26 return Find(n); 27 } 28 LookUpOrCreate(int n,const Terminator & terminator,bool & wasExtant)29 ExternalFileUnit &LookUpOrCreate( 30 int n, const Terminator &terminator, bool &wasExtant) { 31 CriticalSection critical{lock_}; 32 auto *p{Find(n)}; 33 wasExtant = p != nullptr; 34 return p ? *p : Create(n, terminator); 35 } 36 37 // Unit look-up by name is needed for INQUIRE(FILE="...") LookUp(const char * path)38 ExternalFileUnit *LookUp(const char *path) { 39 CriticalSection critical{lock_}; 40 return Find(path); 41 } 42 NewUnit(const Terminator & terminator)43 ExternalFileUnit &NewUnit(const Terminator &terminator) { 44 CriticalSection critical{lock_}; 45 return Create(nextNewUnit_--, terminator); 46 } 47 48 // To prevent races, the unit is removed from the map if it exists, 49 // and put on the closing_ list until DestroyClosed() is called. 50 ExternalFileUnit *LookUpForClose(int); 51 52 void DestroyClosed(ExternalFileUnit &); 53 void CloseAll(IoErrorHandler &); 54 void FlushAll(IoErrorHandler &); 55 56 private: 57 struct Chain { ChainChain58 explicit Chain(int n) : unit{n} {} 59 ExternalFileUnit unit; 60 OwningPtr<Chain> next{nullptr}; 61 }; 62 63 static constexpr int buckets_{1031}; // must be prime Hash(int n)64 int Hash(int n) { return std::abs(n) % buckets_; } 65 Find(int n)66 ExternalFileUnit *Find(int n) { 67 Chain *previous{nullptr}; 68 int hash{Hash(n)}; 69 for (Chain *p{bucket_[hash].get()}; p; previous = p, p = p->next.get()) { 70 if (p->unit.unitNumber() == n) { 71 if (previous) { 72 // Move found unit to front of chain for quicker lookup next time 73 previous->next.swap(p->next); // now p->next.get() == p 74 bucket_[hash].swap(p->next); // now bucket_[hash].get() == p 75 } 76 return &p->unit; 77 } 78 } 79 return nullptr; 80 } 81 ExternalFileUnit *Find(const char *path); 82 83 ExternalFileUnit &Create(int, const Terminator &); 84 85 Lock lock_; 86 OwningPtr<Chain> bucket_[buckets_]{}; // all owned by *this 87 int nextNewUnit_{-1000}; // see 12.5.6.12 in Fortran 2018 88 OwningPtr<Chain> closing_{nullptr}; // units during CLOSE statement 89 }; 90 } // namespace Fortran::runtime::io 91 #endif // FORTRAN_RUNTIME_UNIT_MAP_H_ 92