summaryrefslogtreecommitdiff
path: root/flang/runtime/internal-unit.cpp
blob: 737f0856e33fb221a3f5ffbfd07b74b8197fb915 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//===-- runtime/internal-unit.cpp -------------------------------*- 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
//
//===----------------------------------------------------------------------===//

#include "internal-unit.h"
#include "descriptor.h"
#include "io-error.h"
#include <algorithm>
#include <type_traits>

namespace Fortran::runtime::io {

template<bool isInput>
InternalDescriptorUnit<isInput>::InternalDescriptorUnit(
    Scalar scalar, std::size_t length) {
  recordLength = length;
  endfileRecordNumber = 2;
  void *pointer{reinterpret_cast<void *>(const_cast<char *>(scalar))};
  descriptor().Establish(TypeCode{CFI_type_char}, length, pointer, 0, nullptr,
      CFI_attribute_pointer);
}

template<bool isInput>
InternalDescriptorUnit<isInput>::InternalDescriptorUnit(
    const Descriptor &that, const Terminator &terminator) {
  RUNTIME_CHECK(terminator, that.type().IsCharacter());
  Descriptor &d{descriptor()};
  RUNTIME_CHECK(
      terminator, that.SizeInBytes() <= d.SizeInBytes(maxRank, true, 0));
  new (&d) Descriptor{that};
  d.Check();
  recordLength = d.ElementBytes();
  endfileRecordNumber = d.Elements() + 1;
  d.GetLowerBounds(at_);
}

template<bool isInput> void InternalDescriptorUnit<isInput>::EndIoStatement() {
  if constexpr (!isInput) {
    // blank fill
    while (currentRecordNumber < endfileRecordNumber.value_or(0)) {
      char *record{descriptor().template Element<char>(at_)};
      std::fill_n(record + furthestPositionInRecord,
          recordLength.value_or(0) - furthestPositionInRecord, ' ');
      furthestPositionInRecord = 0;
      ++currentRecordNumber;
      descriptor().IncrementSubscripts(at_);
    }
  }
}

template<bool isInput>
bool InternalDescriptorUnit<isInput>::Emit(
    const char *data, std::size_t bytes, IoErrorHandler &handler) {
  if constexpr (isInput) {
    handler.Crash(
        "InternalDescriptorUnit<true>::Emit() called for an input statement");
    return false;
  }
  if (currentRecordNumber >= endfileRecordNumber.value_or(0)) {
    handler.SignalEnd();
    return false;
  }
  char *record{descriptor().template Element<char>(at_)};
  auto furthestAfter{std::max(furthestPositionInRecord,
      positionInRecord + static_cast<std::int64_t>(bytes))};
  bool ok{true};
  if (furthestAfter > static_cast<std::int64_t>(recordLength.value_or(0))) {
    handler.SignalEor();
    furthestAfter = recordLength.value_or(0);
    bytes = std::max(std::int64_t{0}, furthestAfter - positionInRecord);
    ok = false;
  }
  std::memcpy(record + positionInRecord, data, bytes);
  positionInRecord += bytes;
  furthestPositionInRecord = furthestAfter;
  return ok;
}

template<bool isInput>
bool InternalDescriptorUnit<isInput>::AdvanceRecord(IoErrorHandler &handler) {
  if (currentRecordNumber >= endfileRecordNumber.value_or(0)) {
    handler.SignalEnd();
    return false;
  }
  if (!HandleAbsolutePosition(recordLength.value_or(0), handler)) {
    return false;
  }
  ++currentRecordNumber;
  descriptor().IncrementSubscripts(at_);
  positionInRecord = 0;
  furthestPositionInRecord = 0;
  return true;
}

template<bool isInput>
bool InternalDescriptorUnit<isInput>::HandleAbsolutePosition(
    std::int64_t n, IoErrorHandler &handler) {
  n = std::max<std::int64_t>(0, n);
  bool ok{true};
  if (n > static_cast<std::int64_t>(recordLength.value_or(n))) {
    handler.SignalEor();
    n = *recordLength;
    ok = false;
  }
  if (n > furthestPositionInRecord && ok) {
    if constexpr (!isInput) {
      char *record{descriptor().template Element<char>(at_)};
      std::fill_n(
          record + furthestPositionInRecord, n - furthestPositionInRecord, ' ');
    }
    furthestPositionInRecord = n;
  }
  positionInRecord = n;
  return ok;
}

template<bool isInput>
bool InternalDescriptorUnit<isInput>::HandleRelativePosition(
    std::int64_t n, IoErrorHandler &handler) {
  return HandleAbsolutePosition(positionInRecord + n, handler);
}

template class InternalDescriptorUnit<false>;
template class InternalDescriptorUnit<true>;
}