Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/loader/filemgr.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "loader/filemgr.h"
5
6
#include <algorithm>
7
#include <iterator>
8
9
// Error logging for the file manager needs to be handled by the caller.
10
11
namespace WasmEdge {
12
13
// Set path to file manager. See "include/loader/filemgr.h".
14
0
Expect<void> FileMgr::setPath(const std::filesystem::path &FilePath) {
15
0
  reset();
16
0
  std::error_code ErrCode;
17
0
  Size = std::filesystem::file_size(FilePath, ErrCode);
18
0
  if (likely(!ErrCode)) {
19
0
    if (!MMap::supported()) {
20
0
      Size = 0;
21
0
      Status = ErrCode::Value::IllegalPath;
22
0
      return Unexpect(Status);
23
0
    }
24
0
    FileMap.emplace(FilePath);
25
0
    if (auto *Pointer = FileMap->address(); likely(Pointer)) {
26
0
      Data = reinterpret_cast<const Byte *>(Pointer);
27
0
      Status = ErrCode::Value::Success;
28
0
      return {};
29
0
    } else if (Size == 0) {
30
      // Genuinely empty file: a zero-length mapping fails, and the first read
31
      // will report 'UnexpectedEnd'.
32
0
      FileMap.reset();
33
0
      Data = nullptr;
34
0
      Status = ErrCode::Value::Success;
35
0
      return {};
36
0
    } else {
37
      // A non-empty path that could not be mapped: fail cleanly instead of
38
      // leaving Data null and crashing on the first read.
39
0
      FileMap.reset();
40
0
    }
41
0
  }
42
0
  Size = 0;
43
0
  Status = ErrCode::Value::IllegalPath;
44
0
  return Unexpect(Status);
45
0
}
46
47
// Set code data. See "include/loader/filemgr.h".
48
30.8k
Expect<void> FileMgr::setCode(Span<const Byte> CodeData) {
49
30.8k
  reset();
50
30.8k
  Data = CodeData.data();
51
30.8k
  Size = CodeData.size();
52
30.8k
  Status = ErrCode::Value::Success;
53
30.8k
  return {};
54
30.8k
}
55
56
// Set code data. See "include/loader/filemgr.h".
57
0
Expect<void> FileMgr::setCode(std::vector<Byte> CodeData) {
58
0
  reset();
59
  // Tell GCC 14 that DataHolder has no data.
60
  // Fix the false positive warning,
61
  // which is reported by GCC 14 with `maybe-uninitialized`
62
0
  assuming(!DataHolder);
63
64
0
  DataHolder.emplace(std::move(CodeData));
65
0
  Data = DataHolder->data();
66
0
  Size = DataHolder->size();
67
0
  Status = ErrCode::Value::Success;
68
0
  return {};
69
0
}
70
71
// Read one byte. See "include/loader/filemgr.h".
72
54.0M
Expect<Byte> FileMgr::readByte() {
73
54.0M
  if (unlikely(Status != ErrCode::Value::Success)) {
74
0
    return Unexpect(Status);
75
0
  }
76
  // Set the flag to the start offset.
77
54.0M
  LastPos = Pos;
78
  // Check whether reading exceeds the data or section boundary.
79
54.0M
  EXPECTED_TRY(testRead(1));
80
53.9M
  return Data[Pos++];
81
54.0M
}
82
83
// Read bytes. See "include/loader/filemgr.h".
84
3.04M
Expect<std::vector<Byte>> FileMgr::readBytes(size_t SizeToRead) {
85
3.04M
  if (unlikely(Status != ErrCode::Value::Success)) {
86
0
    return Unexpect(Status);
87
0
  }
88
  // Set the flag to the start offset.
89
3.04M
  LastPos = Pos;
90
  // Check whether reading exceeds the data boundary before allocating.
91
3.04M
  EXPECTED_TRY(testRead(SizeToRead));
92
  // Read bytes into vector.
93
3.04M
  std::vector<Byte> Buf(SizeToRead);
94
3.04M
  EXPECTED_TRY(readBytes(Buf));
95
3.04M
  return Buf;
96
3.04M
}
97
98
// Decode and read an unsigned int. See "include/loader/filemgr.h".
99
26.3M
Expect<uint32_t> FileMgr::readU32() {
100
26.3M
  if (unlikely(Status != ErrCode::Value::Success)) {
101
0
    return Unexpect(Status);
102
0
  }
103
  // Set the flag to the start offset.
104
26.3M
  LastPos = Pos;
105
106
  // Read and decode U32.
107
26.3M
  uint32_t Result = 0;
108
26.3M
  uint32_t Offset = 0;
109
26.3M
  Byte Byte = 0x80;
110
54.6M
  while (Byte & 0x80) {
111
28.2M
    if (unlikely(Offset >= 32)) {
112
22
      Status = ErrCode::Value::IntegerTooLong;
113
22
      return Unexpect(Status);
114
22
    }
115
28.2M
    EXPECTED_TRY(testRead(1));
116
28.2M
    Byte = Data[Pos++];
117
28.2M
    Result |= (Byte & UINT32_C(0x7F)) << Offset;
118
28.2M
    if (Offset == 28 && unlikely((Byte & UINT32_C(0x70)) != 0)) {
119
164
      Status = ErrCode::Value::IntegerTooLarge;
120
164
      return Unexpect(Status);
121
164
    }
122
28.2M
    Offset += 7;
123
28.2M
  }
124
26.3M
  return Result;
125
26.3M
}
126
127
// Decode and read an unsigned long long int. See "include/loader/filemgr.h".
128
755k
Expect<uint64_t> FileMgr::readU64() {
129
755k
  if (Status != ErrCode::Value::Success) {
130
0
    return Unexpect(Status);
131
0
  }
132
  // Set the flag to the start offset.
133
755k
  LastPos = Pos;
134
135
  // Read and decode U64.
136
755k
  uint64_t Result = 0;
137
755k
  uint64_t Offset = 0;
138
755k
  Byte Byte = 0x80;
139
1.62M
  while (Byte & 0x80) {
140
867k
    if (unlikely(Offset >= 64)) {
141
4
      Status = ErrCode::Value::IntegerTooLong;
142
4
      return Unexpect(Status);
143
4
    }
144
867k
    EXPECTED_TRY(testRead(1));
145
866k
    Byte = Data[Pos++];
146
866k
    Result |= (Byte & UINT64_C(0x7F)) << Offset;
147
866k
    if (Offset == 63 && unlikely((Byte & UINT32_C(0x7E)) != 0)) {
148
11
      Status = ErrCode::Value::IntegerTooLarge;
149
11
      return Unexpect(Status);
150
11
    }
151
866k
    Offset += 7;
152
866k
  }
153
755k
  return Result;
154
755k
}
155
156
6.65M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
157
6.65M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
158
6.65M
  static_assert(8 * sizeof(RetType) >= N,
159
6.65M
                "RetType cannot hold the full range of S_N");
160
6.65M
  static_assert(std::is_signed_v<RetType>,
161
6.65M
                "RetType of S_N must be signed type");
162
163
6.65M
  if (Status != ErrCode::Value::Success) {
164
0
    return Unexpect(Status);
165
0
  }
166
  // Set the flag to the start offset.
167
6.65M
  LastPos = Pos;
168
169
  // Read and decode S_N.
170
6.65M
  RetType Result = 0;
171
6.65M
  size_t Offset = 0;
172
6.65M
  size_t RemainingBits = N;
173
8.09M
  while (true) {
174
8.09M
    if (RemainingBits <= 0) {
175
0
      Status = ErrCode::Value::IntegerTooLong;
176
0
      return Unexpect(Status);
177
0
    }
178
179
    // In the remaining logic, RemainingBits must be at least 1.
180
8.09M
    EXPECTED_TRY(testRead(1));
181
8.09M
    WasmEdge::Byte Byte = Data[Pos++];
182
183
8.09M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
184
8.09M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
185
8.09M
    if (Byte & HighestBitMask) {
186
      // The byte has a leading 1. It contains a 7-bit payload.
187
188
1.43M
      if (unlikely(RemainingBits < 7)) {
189
106
        Status = ErrCode::Value::IntegerTooLong;
190
106
        return Unexpect(Status);
191
106
      }
192
193
1.43M
      std::make_unsigned_t<RetType> Payload =
194
1.43M
          Byte & (~HighestBitMask); // & 0b01111111
195
1.43M
      Result |= (Payload << Offset);
196
1.43M
      Offset += 7;
197
1.43M
      RemainingBits -= 7;
198
6.65M
    } else {
199
      // The byte has a leading 0. It will be the last byte.
200
201
      // The number of bits that take effect in the byte. Since RemainingBits
202
      // must be at least 1, EffectiveBits also must be at least 1. It is also
203
      // at most 7.
204
6.65M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
205
6.65M
      std::make_unsigned_t<RetType> Payload = Byte;
206
6.65M
      if (Byte & SecondHighestBitMask) {
207
        // The byte is signed.
208
4.05M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
209
4.05M
          Payload -= (1 << 7);
210
4.05M
        } else {
211
30
          Status = ErrCode::Value::IntegerTooLarge;
212
30
          return Unexpect(Status);
213
30
        }
214
4.05M
      } else {
215
        // The byte is unsigned.
216
2.60M
        if (Byte >= (1 << (EffectiveBits - 1))) {
217
23
          Status = ErrCode::Value::IntegerTooLarge;
218
23
          return Unexpect(Status);
219
23
        }
220
2.60M
      }
221
6.65M
      Result |= (Payload << Offset);
222
6.65M
      break;
223
6.65M
    }
224
8.09M
  }
225
226
6.65M
  return Result;
227
6.65M
}
cxx20::expected<long, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<long, 33ul>()
Line
Count
Source
156
3.34M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
157
3.34M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
158
3.34M
  static_assert(8 * sizeof(RetType) >= N,
159
3.34M
                "RetType cannot hold the full range of S_N");
160
3.34M
  static_assert(std::is_signed_v<RetType>,
161
3.34M
                "RetType of S_N must be signed type");
162
163
3.34M
  if (Status != ErrCode::Value::Success) {
164
0
    return Unexpect(Status);
165
0
  }
166
  // Set the flag to the start offset.
167
3.34M
  LastPos = Pos;
168
169
  // Read and decode S_N.
170
3.34M
  RetType Result = 0;
171
3.34M
  size_t Offset = 0;
172
3.34M
  size_t RemainingBits = N;
173
3.42M
  while (true) {
174
3.42M
    if (RemainingBits <= 0) {
175
0
      Status = ErrCode::Value::IntegerTooLong;
176
0
      return Unexpect(Status);
177
0
    }
178
179
    // In the remaining logic, RemainingBits must be at least 1.
180
3.42M
    EXPECTED_TRY(testRead(1));
181
3.42M
    WasmEdge::Byte Byte = Data[Pos++];
182
183
3.42M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
184
3.42M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
185
3.42M
    if (Byte & HighestBitMask) {
186
      // The byte has a leading 1. It contains a 7-bit payload.
187
188
78.9k
      if (unlikely(RemainingBits < 7)) {
189
59
        Status = ErrCode::Value::IntegerTooLong;
190
59
        return Unexpect(Status);
191
59
      }
192
193
78.8k
      std::make_unsigned_t<RetType> Payload =
194
78.8k
          Byte & (~HighestBitMask); // & 0b01111111
195
78.8k
      Result |= (Payload << Offset);
196
78.8k
      Offset += 7;
197
78.8k
      RemainingBits -= 7;
198
3.34M
    } else {
199
      // The byte has a leading 0. It will be the last byte.
200
201
      // The number of bits that take effect in the byte. Since RemainingBits
202
      // must be at least 1, EffectiveBits also must be at least 1. It is also
203
      // at most 7.
204
3.34M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
205
3.34M
      std::make_unsigned_t<RetType> Payload = Byte;
206
3.34M
      if (Byte & SecondHighestBitMask) {
207
        // The byte is signed.
208
1.60M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
209
1.60M
          Payload -= (1 << 7);
210
1.60M
        } else {
211
10
          Status = ErrCode::Value::IntegerTooLarge;
212
10
          return Unexpect(Status);
213
10
        }
214
1.74M
      } else {
215
        // The byte is unsigned.
216
1.74M
        if (Byte >= (1 << (EffectiveBits - 1))) {
217
4
          Status = ErrCode::Value::IntegerTooLarge;
218
4
          return Unexpect(Status);
219
4
        }
220
1.74M
      }
221
3.34M
      Result |= (Payload << Offset);
222
3.34M
      break;
223
3.34M
    }
224
3.42M
  }
225
226
3.34M
  return Result;
227
3.34M
}
cxx20::expected<int, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<int, 32ul>()
Line
Count
Source
156
2.39M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
157
2.39M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
158
2.39M
  static_assert(8 * sizeof(RetType) >= N,
159
2.39M
                "RetType cannot hold the full range of S_N");
160
2.39M
  static_assert(std::is_signed_v<RetType>,
161
2.39M
                "RetType of S_N must be signed type");
162
163
2.39M
  if (Status != ErrCode::Value::Success) {
164
0
    return Unexpect(Status);
165
0
  }
166
  // Set the flag to the start offset.
167
2.39M
  LastPos = Pos;
168
169
  // Read and decode S_N.
170
2.39M
  RetType Result = 0;
171
2.39M
  size_t Offset = 0;
172
2.39M
  size_t RemainingBits = N;
173
2.71M
  while (true) {
174
2.71M
    if (RemainingBits <= 0) {
175
0
      Status = ErrCode::Value::IntegerTooLong;
176
0
      return Unexpect(Status);
177
0
    }
178
179
    // In the remaining logic, RemainingBits must be at least 1.
180
2.71M
    EXPECTED_TRY(testRead(1));
181
2.71M
    WasmEdge::Byte Byte = Data[Pos++];
182
183
2.71M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
184
2.71M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
185
2.71M
    if (Byte & HighestBitMask) {
186
      // The byte has a leading 1. It contains a 7-bit payload.
187
188
318k
      if (unlikely(RemainingBits < 7)) {
189
18
        Status = ErrCode::Value::IntegerTooLong;
190
18
        return Unexpect(Status);
191
18
      }
192
193
318k
      std::make_unsigned_t<RetType> Payload =
194
318k
          Byte & (~HighestBitMask); // & 0b01111111
195
318k
      Result |= (Payload << Offset);
196
318k
      Offset += 7;
197
318k
      RemainingBits -= 7;
198
2.39M
    } else {
199
      // The byte has a leading 0. It will be the last byte.
200
201
      // The number of bits that take effect in the byte. Since RemainingBits
202
      // must be at least 1, EffectiveBits also must be at least 1. It is also
203
      // at most 7.
204
2.39M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
205
2.39M
      std::make_unsigned_t<RetType> Payload = Byte;
206
2.39M
      if (Byte & SecondHighestBitMask) {
207
        // The byte is signed.
208
1.84M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
209
1.84M
          Payload -= (1 << 7);
210
1.84M
        } else {
211
9
          Status = ErrCode::Value::IntegerTooLarge;
212
9
          return Unexpect(Status);
213
9
        }
214
1.84M
      } else {
215
        // The byte is unsigned.
216
556k
        if (Byte >= (1 << (EffectiveBits - 1))) {
217
8
          Status = ErrCode::Value::IntegerTooLarge;
218
8
          return Unexpect(Status);
219
8
        }
220
556k
      }
221
2.39M
      Result |= (Payload << Offset);
222
2.39M
      break;
223
2.39M
    }
224
2.71M
  }
225
226
2.39M
  return Result;
227
2.39M
}
cxx20::expected<long, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<long, 64ul>()
Line
Count
Source
156
913k
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
157
913k
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
158
913k
  static_assert(8 * sizeof(RetType) >= N,
159
913k
                "RetType cannot hold the full range of S_N");
160
913k
  static_assert(std::is_signed_v<RetType>,
161
913k
                "RetType of S_N must be signed type");
162
163
913k
  if (Status != ErrCode::Value::Success) {
164
0
    return Unexpect(Status);
165
0
  }
166
  // Set the flag to the start offset.
167
913k
  LastPos = Pos;
168
169
  // Read and decode S_N.
170
913k
  RetType Result = 0;
171
913k
  size_t Offset = 0;
172
913k
  size_t RemainingBits = N;
173
1.95M
  while (true) {
174
1.95M
    if (RemainingBits <= 0) {
175
0
      Status = ErrCode::Value::IntegerTooLong;
176
0
      return Unexpect(Status);
177
0
    }
178
179
    // In the remaining logic, RemainingBits must be at least 1.
180
1.95M
    EXPECTED_TRY(testRead(1));
181
1.95M
    WasmEdge::Byte Byte = Data[Pos++];
182
183
1.95M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
184
1.95M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
185
1.95M
    if (Byte & HighestBitMask) {
186
      // The byte has a leading 1. It contains a 7-bit payload.
187
188
1.03M
      if (unlikely(RemainingBits < 7)) {
189
29
        Status = ErrCode::Value::IntegerTooLong;
190
29
        return Unexpect(Status);
191
29
      }
192
193
1.03M
      std::make_unsigned_t<RetType> Payload =
194
1.03M
          Byte & (~HighestBitMask); // & 0b01111111
195
1.03M
      Result |= (Payload << Offset);
196
1.03M
      Offset += 7;
197
1.03M
      RemainingBits -= 7;
198
1.03M
    } else {
199
      // The byte has a leading 0. It will be the last byte.
200
201
      // The number of bits that take effect in the byte. Since RemainingBits
202
      // must be at least 1, EffectiveBits also must be at least 1. It is also
203
      // at most 7.
204
913k
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
205
913k
      std::make_unsigned_t<RetType> Payload = Byte;
206
913k
      if (Byte & SecondHighestBitMask) {
207
        // The byte is signed.
208
611k
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
209
611k
          Payload -= (1 << 7);
210
611k
        } else {
211
11
          Status = ErrCode::Value::IntegerTooLarge;
212
11
          return Unexpect(Status);
213
11
        }
214
611k
      } else {
215
        // The byte is unsigned.
216
302k
        if (Byte >= (1 << (EffectiveBits - 1))) {
217
11
          Status = ErrCode::Value::IntegerTooLarge;
218
11
          return Unexpect(Status);
219
11
        }
220
302k
      }
221
913k
      Result |= (Payload << Offset);
222
913k
      break;
223
913k
    }
224
1.95M
  }
225
226
913k
  return Result;
227
913k
}
228
229
3.34M
Expect<int64_t> FileMgr::readS33() { return readSN<int64_t, 33>(); }
230
231
// Decode and read a signed int. See "include/loader/filemgr.h".
232
2.39M
Expect<int32_t> FileMgr::readS32() { return readSN<int32_t, 32>(); }
233
234
// Decode and read a signed long long int. See "include/loader/filemgr.h".
235
913k
Expect<int64_t> FileMgr::readS64() { return readSN<int64_t, 64>(); }
236
237
// Copy bytes to a float. See "include/loader/filemgr.h".
238
105k
Expect<float> FileMgr::readF32() {
239
105k
  if (Status != ErrCode::Value::Success) {
240
0
    return Unexpect(Status);
241
0
  }
242
  // Set the flag to the start offset.
243
105k
  LastPos = Pos;
244
245
105k
  uint32_t Buf = 0;
246
105k
  Byte Byte = 0x00;
247
  // Check whether reading exceeds the data or section boundary.
248
105k
  EXPECTED_TRY(testRead(4));
249
524k
  for (uint32_t I = 0; I < 4; I++) {
250
419k
    Byte = Data[Pos++];
251
419k
    Buf |= (Byte & UINT32_C(0xFF)) << (I * UINT32_C(8));
252
419k
  }
253
104k
  float Result;
254
104k
  static_assert(sizeof(Buf) == sizeof(Result));
255
104k
  std::memcpy(&Result, &Buf, sizeof(Result));
256
104k
  return Result;
257
105k
}
258
259
// Copy bytes to a double. See "include/loader/filemgr.h".
260
72.0k
Expect<double> FileMgr::readF64() {
261
72.0k
  if (Status != ErrCode::Value::Success) {
262
0
    return Unexpect(Status);
263
0
  }
264
  // Set the flag to the start offset.
265
72.0k
  LastPos = Pos;
266
267
72.0k
  uint64_t Buf = 0;
268
72.0k
  Byte Byte = 0x00;
269
  // Check whether reading exceeds the data or section boundary.
270
72.0k
  EXPECTED_TRY(testRead(8));
271
648k
  for (uint32_t I = 0; I < 8; I++) {
272
576k
    Byte = Data[Pos++];
273
576k
    Buf |= (Byte & UINT64_C(0xFF)) << (I * UINT64_C(8));
274
576k
  }
275
72.0k
  double Result;
276
72.0k
  static_assert(sizeof(Buf) == sizeof(Result));
277
72.0k
  std::memcpy(&Result, &Buf, sizeof(Result));
278
72.0k
  return Result;
279
72.0k
}
280
281
// Read a vector of bytes. See "include/loader/filemgr.h".
282
3.59M
Expect<std::string> FileMgr::readName() {
283
3.59M
  if (unlikely(Status != ErrCode::Value::Success)) {
284
0
    return Unexpect(Status);
285
0
  }
286
  // If UTF-8 validation, readU32(), or readBytes() failed, the last successful
287
  // reading offset will be at the start of `Name`.
288
3.59M
  LastPos = Pos;
289
290
  // Read the name size.
291
3.59M
  EXPECTED_TRY(uint32_t SizeToRead, readU32());
292
293
  // Check whether the string length exceeds the data boundary.
294
3.59M
  if (auto Res = testRead(SizeToRead); unlikely(!Res)) {
295
299
    return Unexpect(ErrCode::Value::LengthOutOfBounds);
296
299
  }
297
298
  // Read the UTF-8 bytes.
299
3.59M
  std::string Str(SizeToRead, '\0');
300
3.59M
  EXPECTED_TRY(
301
3.59M
      readBytes(Span<Byte>(reinterpret_cast<Byte *>(Str.data()), Str.size())));
302
303
  // UTF-8 validation.
304
3.59M
  bool Valid = true;
305
5.83M
  for (uint32_t I = 0; I < Str.size() && Valid; ++I) {
306
2.24M
    char C = Str.data()[I];
307
2.24M
    uint32_t N = 0;
308
2.24M
    if ((C & '\x80') == 0) {
309
      // 0xxxxxxx, 7 bits UCS, ASCII
310
2.22M
      N = 0;
311
2.22M
    } else if ((C & '\xE0') == '\xC0') {
312
      // 110xxxxx, 11 bits UCS, U+80 to U+7FF
313
3.19k
      N = 1;
314
11.5k
    } else if ((C & '\xF0') == '\xE0') {
315
      // 1110xxxx, 16 bits UCS, U+800 to U+D7FF and U+E000 to U+FFFF
316
7.93k
      N = 2;
317
7.93k
    } else if ((C & '\xF8') == '\xF0') {
318
      // 11110xxx, 21 bits UCS, U+10000 to U+10FFFF
319
3.48k
      N = 3;
320
3.48k
    } else {
321
170
      Valid = false;
322
170
    }
323
324
    // Need N more bytes.
325
2.24M
    if (I + N >= Str.size()) {
326
44
      Valid = false;
327
44
    }
328
    // Invalid ranges
329
2.24M
    if (N == 1 && (C & '\xDE') == '\xC0') {
330
      // 11 bits UCS, U+0 to U+80, FAIL
331
4
      Valid = false;
332
2.24M
    } else if (N == 2 &&
333
7.93k
               ((C == '\xE0' && (Str.data()[I + 1] & '\xA0') == '\x80') ||
334
                // 16 bits UCS, U+0 to U+7FF, FAIL
335
7.93k
                (C == '\xED' && (Str.data()[I + 1] & '\xA0') == '\xA0')
336
                // 16 bits UCS, U+D800 to U+DFFF, FAIL
337
7.93k
                )) {
338
12
      Valid = false;
339
2.24M
    } else if (N == 3 &&
340
3.48k
               ((C == '\xF0' && (Str.data()[I + 1] & '\xB0') == '\x80') ||
341
                // 21 bits UCS, U+0 to U+FFFF, FAIL
342
3.47k
                (C == '\xF4' && (Str.data()[I + 1] & '\xB0') != '\x80') ||
343
                // 21 bits UCS, U+110000 to U+13FFFF, FAIL
344
3.46k
                (C != '\xF4' && (C & '\xF4') == '\xF4')
345
                // 21 bits UCS, U+140000 to U+1FFFFF, FAIL
346
3.48k
                )) {
347
23
      Valid = false;
348
23
    }
349
350
2.26M
    for (uint32_t J = 0; J < N && Valid; ++J) {
351
      // N bytes need to match 10xxxxxx
352
29.2k
      if ((Str.data()[I + J + 1] & '\xC0') != '\x80') {
353
124
        Valid = false;
354
124
      }
355
29.2k
    }
356
2.24M
    I += N;
357
2.24M
  }
358
359
3.59M
  if (!Valid) {
360
369
    Status = ErrCode::Value::MalformedUTF8;
361
369
    return Unexpect(Status);
362
369
  }
363
3.59M
  return Str;
364
3.59M
}
365
366
// Peek one byte. See "include/loader/filemgr.h".
367
2.17M
Expect<Byte> FileMgr::peekByte() {
368
2.17M
  EXPECTED_TRY(Byte B, readByte());
369
2.17M
  Pos--;
370
2.17M
  return B;
371
2.17M
}
372
373
// Get the file header type. See "include/loader/filemgr.h".
374
30.8k
FileMgr::FileHeader FileMgr::getHeaderType() {
375
30.8k
  if (Size >= 4) {
376
30.7k
    Byte WASMMagic[] = {0x00, 0x61, 0x73, 0x6D};
377
30.7k
    Byte ELFMagic[] = {0x7F, 0x45, 0x4C, 0x46};
378
30.7k
    Byte MAC32agic[] = {0xCE, 0xFA, 0xED, 0xFE};
379
30.7k
    Byte MAC64agic[] = {0xCF, 0xFA, 0xED, 0xFE};
380
30.7k
    if (std::equal(WASMMagic, WASMMagic + 4, Data)) {
381
30.7k
      return FileMgr::FileHeader::Wasm;
382
30.7k
    } else if (std::equal(ELFMagic, ELFMagic + 4, Data)) {
383
2
      return FileMgr::FileHeader::ELF;
384
85
    } else if (std::equal(MAC32agic, MAC32agic + 4, Data)) {
385
2
      return FileMgr::FileHeader::MachO_32;
386
83
    } else if (std::equal(MAC64agic, MAC64agic + 4, Data)) {
387
4
      return FileMgr::FileHeader::MachO_64;
388
4
    }
389
30.7k
  }
390
169
  if (Size >= 2) {
391
151
    Byte DLLMagic[] = {0x4D, 0x5A};
392
151
    if (std::equal(DLLMagic, DLLMagic + 2, Data)) {
393
3
      return FileMgr::FileHeader::DLL;
394
3
    }
395
151
  }
396
166
  return FileMgr::FileHeader::Unknown;
397
169
}
398
399
// Jump a section. See "include/loader/filemgr.h".
400
0
Expect<void> FileMgr::jumpContent() {
401
0
  if (unlikely(Status != ErrCode::Value::Success)) {
402
0
    return Unexpect(Status);
403
0
  }
404
  // Set the flag to the start offset.
405
0
  LastPos = Pos;
406
  // Read the section size.
407
0
  EXPECTED_TRY(uint32_t SecSize, readU32());
408
  // Jump the content.
409
0
  EXPECTED_TRY(testRead(SecSize));
410
0
  Pos += SecSize;
411
0
  return {};
412
0
}
413
414
// Helper function for reading bytes. See "include/loader/filemgr.h".
415
6.64M
Expect<void> FileMgr::readBytes(Span<Byte> Buffer) {
416
6.64M
  if (unlikely(Status != ErrCode::Value::Success)) {
417
0
    return Unexpect(Status);
418
0
  }
419
  // The adjustment of `LastPos` should be handled by the caller.
420
6.64M
  auto SizeToRead = Buffer.size();
421
6.64M
  if (likely(SizeToRead > 0)) {
422
    // Check whether reading exceeds the data boundary.
423
1.11M
    EXPECTED_TRY(testRead(SizeToRead));
424
1.11M
    std::copy_n(Data + Pos, SizeToRead, Buffer.begin());
425
1.11M
    Pos += SizeToRead;
426
1.11M
  }
427
6.64M
  return {};
428
6.64M
}
429
430
// Helper function for checking boundary. See "include/loader/filemgr.h".
431
99.2M
Expect<void> FileMgr::testRead(uint64_t Read) {
432
  // Check whether reading exceeds the data boundary.
433
99.2M
  if (unlikely(getRemainSize() < Read)) {
434
23.1k
    Pos = Size;
435
23.1k
    LastPos = Pos;
436
23.1k
    Status = ErrCode::Value::UnexpectedEnd;
437
23.1k
    return Unexpect(Status);
438
23.1k
  }
439
99.1M
  return {};
440
99.2M
}
441
442
} // namespace WasmEdge