Coverage Report

Created: 2026-08-13 06:09

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
23.4k
Expect<void> FileMgr::setCode(Span<const Byte> CodeData) {
49
23.4k
  reset();
50
23.4k
  Data = CodeData.data();
51
23.4k
  Size = CodeData.size();
52
23.4k
  Status = ErrCode::Value::Success;
53
23.4k
  return {};
54
23.4k
}
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
34.5M
Expect<Byte> FileMgr::readByte() {
73
34.5M
  if (unlikely(Status != ErrCode::Value::Success)) {
74
0
    return Unexpect(Status);
75
0
  }
76
  // Set the flag to the start offset.
77
34.5M
  LastPos = Pos;
78
  // Check whether reading exceeds the data or section boundary.
79
34.5M
  EXPECTED_TRY(testRead(1));
80
34.4M
  return Data[Pos++];
81
34.5M
}
82
83
// Read bytes. See "include/loader/filemgr.h".
84
1.46M
Expect<std::vector<Byte>> FileMgr::readBytes(size_t SizeToRead) {
85
  // Set the flag to the start offset.
86
1.46M
  LastPos = Pos;
87
  // Read bytes into vector.
88
1.46M
  std::vector<Byte> Buf(SizeToRead);
89
1.46M
  EXPECTED_TRY(readBytes(Buf));
90
1.46M
  return Buf;
91
1.46M
}
92
93
// Decode and read an unsigned int. See "include/loader/filemgr.h".
94
11.5M
Expect<uint32_t> FileMgr::readU32() {
95
11.5M
  if (unlikely(Status != ErrCode::Value::Success)) {
96
0
    return Unexpect(Status);
97
0
  }
98
  // Set the flag to the start offset.
99
11.5M
  LastPos = Pos;
100
101
  // Read and decode U32.
102
11.5M
  uint32_t Result = 0;
103
11.5M
  uint32_t Offset = 0;
104
11.5M
  Byte Byte = 0x80;
105
24.1M
  while (Byte & 0x80) {
106
12.5M
    if (unlikely(Offset >= 32)) {
107
19
      Status = ErrCode::Value::IntegerTooLong;
108
19
      return Unexpect(Status);
109
19
    }
110
12.5M
    EXPECTED_TRY(testRead(1));
111
12.5M
    Byte = Data[Pos++];
112
12.5M
    Result |= (Byte & UINT32_C(0x7F)) << Offset;
113
12.5M
    if (Offset == 28 && unlikely((Byte & UINT32_C(0x70)) != 0)) {
114
175
      Status = ErrCode::Value::IntegerTooLarge;
115
175
      return Unexpect(Status);
116
175
    }
117
12.5M
    Offset += 7;
118
12.5M
  }
119
11.5M
  return Result;
120
11.5M
}
121
122
// Decode and read an unsigned long long int. See "include/loader/filemgr.h".
123
472k
Expect<uint64_t> FileMgr::readU64() {
124
472k
  if (Status != ErrCode::Value::Success) {
125
0
    return Unexpect(Status);
126
0
  }
127
  // Set the flag to the start offset.
128
472k
  LastPos = Pos;
129
130
  // Read and decode U64.
131
472k
  uint64_t Result = 0;
132
472k
  uint64_t Offset = 0;
133
472k
  Byte Byte = 0x80;
134
1.00M
  while (Byte & 0x80) {
135
533k
    if (unlikely(Offset >= 64)) {
136
3
      Status = ErrCode::Value::IntegerTooLong;
137
3
      return Unexpect(Status);
138
3
    }
139
533k
    EXPECTED_TRY(testRead(1));
140
533k
    Byte = Data[Pos++];
141
533k
    Result |= (Byte & UINT64_C(0x7F)) << Offset;
142
533k
    if (Offset == 63 && unlikely((Byte & UINT32_C(0x7E)) != 0)) {
143
6
      Status = ErrCode::Value::IntegerTooLarge;
144
6
      return Unexpect(Status);
145
6
    }
146
533k
    Offset += 7;
147
533k
  }
148
472k
  return Result;
149
472k
}
150
151
4.68M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
152
4.68M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
153
4.68M
  static_assert(8 * sizeof(RetType) >= N,
154
4.68M
                "RetType cannot hold the full range of S_N");
155
4.68M
  static_assert(std::is_signed_v<RetType>,
156
4.68M
                "RetType of S_N must be signed type");
157
158
4.68M
  if (Status != ErrCode::Value::Success) {
159
0
    return Unexpect(Status);
160
0
  }
161
  // Set the flag to the start offset.
162
4.68M
  LastPos = Pos;
163
164
  // Read and decode S_N.
165
4.68M
  RetType Result = 0;
166
4.68M
  size_t Offset = 0;
167
4.68M
  size_t RemainingBits = N;
168
5.64M
  while (true) {
169
5.64M
    if (RemainingBits <= 0) {
170
0
      Status = ErrCode::Value::IntegerTooLong;
171
0
      return Unexpect(Status);
172
0
    }
173
174
    // In the remaining logic, RemainingBits must be at least 1.
175
5.64M
    EXPECTED_TRY(testRead(1));
176
5.64M
    WasmEdge::Byte Byte = Data[Pos++];
177
178
5.64M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
179
5.64M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
180
5.64M
    if (Byte & HighestBitMask) {
181
      // The byte has a leading 1. It contains a 7-bit payload.
182
183
954k
      if (unlikely(RemainingBits < 7)) {
184
91
        Status = ErrCode::Value::IntegerTooLong;
185
91
        return Unexpect(Status);
186
91
      }
187
188
954k
      std::make_unsigned_t<RetType> Payload =
189
954k
          Byte & (~HighestBitMask); // & 0b01111111
190
954k
      Result |= (Payload << Offset);
191
954k
      Offset += 7;
192
954k
      RemainingBits -= 7;
193
4.68M
    } else {
194
      // The byte has a leading 0. It will be the last byte.
195
196
      // The number of bits that take effect in the byte. Since RemainingBits
197
      // must be at least 1, EffectiveBits also must be at least 1. It is also
198
      // at most 7.
199
4.68M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
200
4.68M
      std::make_unsigned_t<RetType> Payload = Byte;
201
4.68M
      if (Byte & SecondHighestBitMask) {
202
        // The byte is signed.
203
3.15M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
204
3.15M
          Payload -= (1 << 7);
205
3.15M
        } else {
206
34
          Status = ErrCode::Value::IntegerTooLarge;
207
34
          return Unexpect(Status);
208
34
        }
209
3.15M
      } else {
210
        // The byte is unsigned.
211
1.53M
        if (Byte >= (1 << (EffectiveBits - 1))) {
212
29
          Status = ErrCode::Value::IntegerTooLarge;
213
29
          return Unexpect(Status);
214
29
        }
215
1.53M
      }
216
4.68M
      Result |= (Payload << Offset);
217
4.68M
      break;
218
4.68M
    }
219
5.64M
  }
220
221
4.68M
  return Result;
222
4.68M
}
cxx20::expected<long, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<long, 33ul>()
Line
Count
Source
151
1.46M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
152
1.46M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
153
1.46M
  static_assert(8 * sizeof(RetType) >= N,
154
1.46M
                "RetType cannot hold the full range of S_N");
155
1.46M
  static_assert(std::is_signed_v<RetType>,
156
1.46M
                "RetType of S_N must be signed type");
157
158
1.46M
  if (Status != ErrCode::Value::Success) {
159
0
    return Unexpect(Status);
160
0
  }
161
  // Set the flag to the start offset.
162
1.46M
  LastPos = Pos;
163
164
  // Read and decode S_N.
165
1.46M
  RetType Result = 0;
166
1.46M
  size_t Offset = 0;
167
1.46M
  size_t RemainingBits = N;
168
1.54M
  while (true) {
169
1.54M
    if (RemainingBits <= 0) {
170
0
      Status = ErrCode::Value::IntegerTooLong;
171
0
      return Unexpect(Status);
172
0
    }
173
174
    // In the remaining logic, RemainingBits must be at least 1.
175
1.54M
    EXPECTED_TRY(testRead(1));
176
1.54M
    WasmEdge::Byte Byte = Data[Pos++];
177
178
1.54M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
179
1.54M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
180
1.54M
    if (Byte & HighestBitMask) {
181
      // The byte has a leading 1. It contains a 7-bit payload.
182
183
78.4k
      if (unlikely(RemainingBits < 7)) {
184
41
        Status = ErrCode::Value::IntegerTooLong;
185
41
        return Unexpect(Status);
186
41
      }
187
188
78.4k
      std::make_unsigned_t<RetType> Payload =
189
78.4k
          Byte & (~HighestBitMask); // & 0b01111111
190
78.4k
      Result |= (Payload << Offset);
191
78.4k
      Offset += 7;
192
78.4k
      RemainingBits -= 7;
193
1.46M
    } else {
194
      // The byte has a leading 0. It will be the last byte.
195
196
      // The number of bits that take effect in the byte. Since RemainingBits
197
      // must be at least 1, EffectiveBits also must be at least 1. It is also
198
      // at most 7.
199
1.46M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
200
1.46M
      std::make_unsigned_t<RetType> Payload = Byte;
201
1.46M
      if (Byte & SecondHighestBitMask) {
202
        // The byte is signed.
203
580k
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
204
580k
          Payload -= (1 << 7);
205
580k
        } else {
206
12
          Status = ErrCode::Value::IntegerTooLarge;
207
12
          return Unexpect(Status);
208
12
        }
209
885k
      } else {
210
        // The byte is unsigned.
211
885k
        if (Byte >= (1 << (EffectiveBits - 1))) {
212
10
          Status = ErrCode::Value::IntegerTooLarge;
213
10
          return Unexpect(Status);
214
10
        }
215
885k
      }
216
1.46M
      Result |= (Payload << Offset);
217
1.46M
      break;
218
1.46M
    }
219
1.54M
  }
220
221
1.46M
  return Result;
222
1.46M
}
cxx20::expected<int, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<int, 32ul>()
Line
Count
Source
151
2.38M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
152
2.38M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
153
2.38M
  static_assert(8 * sizeof(RetType) >= N,
154
2.38M
                "RetType cannot hold the full range of S_N");
155
2.38M
  static_assert(std::is_signed_v<RetType>,
156
2.38M
                "RetType of S_N must be signed type");
157
158
2.38M
  if (Status != ErrCode::Value::Success) {
159
0
    return Unexpect(Status);
160
0
  }
161
  // Set the flag to the start offset.
162
2.38M
  LastPos = Pos;
163
164
  // Read and decode S_N.
165
2.38M
  RetType Result = 0;
166
2.38M
  size_t Offset = 0;
167
2.38M
  size_t RemainingBits = N;
168
2.58M
  while (true) {
169
2.58M
    if (RemainingBits <= 0) {
170
0
      Status = ErrCode::Value::IntegerTooLong;
171
0
      return Unexpect(Status);
172
0
    }
173
174
    // In the remaining logic, RemainingBits must be at least 1.
175
2.58M
    EXPECTED_TRY(testRead(1));
176
2.58M
    WasmEdge::Byte Byte = Data[Pos++];
177
178
2.58M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
179
2.58M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
180
2.58M
    if (Byte & HighestBitMask) {
181
      // The byte has a leading 1. It contains a 7-bit payload.
182
183
197k
      if (unlikely(RemainingBits < 7)) {
184
18
        Status = ErrCode::Value::IntegerTooLong;
185
18
        return Unexpect(Status);
186
18
      }
187
188
197k
      std::make_unsigned_t<RetType> Payload =
189
197k
          Byte & (~HighestBitMask); // & 0b01111111
190
197k
      Result |= (Payload << Offset);
191
197k
      Offset += 7;
192
197k
      RemainingBits -= 7;
193
2.38M
    } else {
194
      // The byte has a leading 0. It will be the last byte.
195
196
      // The number of bits that take effect in the byte. Since RemainingBits
197
      // must be at least 1, EffectiveBits also must be at least 1. It is also
198
      // at most 7.
199
2.38M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
200
2.38M
      std::make_unsigned_t<RetType> Payload = Byte;
201
2.38M
      if (Byte & SecondHighestBitMask) {
202
        // The byte is signed.
203
1.91M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
204
1.91M
          Payload -= (1 << 7);
205
1.91M
        } else {
206
7
          Status = ErrCode::Value::IntegerTooLarge;
207
7
          return Unexpect(Status);
208
7
        }
209
1.91M
      } else {
210
        // The byte is unsigned.
211
472k
        if (Byte >= (1 << (EffectiveBits - 1))) {
212
8
          Status = ErrCode::Value::IntegerTooLarge;
213
8
          return Unexpect(Status);
214
8
        }
215
472k
      }
216
2.38M
      Result |= (Payload << Offset);
217
2.38M
      break;
218
2.38M
    }
219
2.58M
  }
220
221
2.38M
  return Result;
222
2.38M
}
cxx20::expected<long, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<long, 64ul>()
Line
Count
Source
151
833k
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
152
833k
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
153
833k
  static_assert(8 * sizeof(RetType) >= N,
154
833k
                "RetType cannot hold the full range of S_N");
155
833k
  static_assert(std::is_signed_v<RetType>,
156
833k
                "RetType of S_N must be signed type");
157
158
833k
  if (Status != ErrCode::Value::Success) {
159
0
    return Unexpect(Status);
160
0
  }
161
  // Set the flag to the start offset.
162
833k
  LastPos = Pos;
163
164
  // Read and decode S_N.
165
833k
  RetType Result = 0;
166
833k
  size_t Offset = 0;
167
833k
  size_t RemainingBits = N;
168
1.51M
  while (true) {
169
1.51M
    if (RemainingBits <= 0) {
170
0
      Status = ErrCode::Value::IntegerTooLong;
171
0
      return Unexpect(Status);
172
0
    }
173
174
    // In the remaining logic, RemainingBits must be at least 1.
175
1.51M
    EXPECTED_TRY(testRead(1));
176
1.51M
    WasmEdge::Byte Byte = Data[Pos++];
177
178
1.51M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
179
1.51M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
180
1.51M
    if (Byte & HighestBitMask) {
181
      // The byte has a leading 1. It contains a 7-bit payload.
182
183
678k
      if (unlikely(RemainingBits < 7)) {
184
32
        Status = ErrCode::Value::IntegerTooLong;
185
32
        return Unexpect(Status);
186
32
      }
187
188
678k
      std::make_unsigned_t<RetType> Payload =
189
678k
          Byte & (~HighestBitMask); // & 0b01111111
190
678k
      Result |= (Payload << Offset);
191
678k
      Offset += 7;
192
678k
      RemainingBits -= 7;
193
833k
    } else {
194
      // The byte has a leading 0. It will be the last byte.
195
196
      // The number of bits that take effect in the byte. Since RemainingBits
197
      // must be at least 1, EffectiveBits also must be at least 1. It is also
198
      // at most 7.
199
833k
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
200
833k
      std::make_unsigned_t<RetType> Payload = Byte;
201
833k
      if (Byte & SecondHighestBitMask) {
202
        // The byte is signed.
203
657k
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
204
657k
          Payload -= (1 << 7);
205
657k
        } else {
206
15
          Status = ErrCode::Value::IntegerTooLarge;
207
15
          return Unexpect(Status);
208
15
        }
209
657k
      } else {
210
        // The byte is unsigned.
211
176k
        if (Byte >= (1 << (EffectiveBits - 1))) {
212
11
          Status = ErrCode::Value::IntegerTooLarge;
213
11
          return Unexpect(Status);
214
11
        }
215
176k
      }
216
833k
      Result |= (Payload << Offset);
217
833k
      break;
218
833k
    }
219
1.51M
  }
220
221
833k
  return Result;
222
833k
}
223
224
1.46M
Expect<int64_t> FileMgr::readS33() { return readSN<int64_t, 33>(); }
225
226
// Decode and read a signed int. See "include/loader/filemgr.h".
227
2.38M
Expect<int32_t> FileMgr::readS32() { return readSN<int32_t, 32>(); }
228
229
// Decode and read a signed long long int. See "include/loader/filemgr.h".
230
833k
Expect<int64_t> FileMgr::readS64() { return readSN<int64_t, 64>(); }
231
232
// Copy bytes to a float. See "include/loader/filemgr.h".
233
45.7k
Expect<float> FileMgr::readF32() {
234
45.7k
  if (Status != ErrCode::Value::Success) {
235
0
    return Unexpect(Status);
236
0
  }
237
  // Set the flag to the start offset.
238
45.7k
  LastPos = Pos;
239
240
45.7k
  uint32_t Buf = 0;
241
45.7k
  Byte Byte = 0x00;
242
  // Check whether reading exceeds the data or section boundary.
243
45.7k
  EXPECTED_TRY(testRead(4));
244
228k
  for (uint32_t I = 0; I < 4; I++) {
245
182k
    Byte = Data[Pos++];
246
182k
    Buf |= (Byte & UINT32_C(0xFF)) << (I * UINT32_C(8));
247
182k
  }
248
45.7k
  float Result;
249
45.7k
  static_assert(sizeof(Buf) == sizeof(Result));
250
45.7k
  std::memcpy(&Result, &Buf, sizeof(Result));
251
45.7k
  return Result;
252
45.7k
}
253
254
// Copy bytes to a double. See "include/loader/filemgr.h".
255
22.0k
Expect<double> FileMgr::readF64() {
256
22.0k
  if (Status != ErrCode::Value::Success) {
257
0
    return Unexpect(Status);
258
0
  }
259
  // Set the flag to the start offset.
260
22.0k
  LastPos = Pos;
261
262
22.0k
  uint64_t Buf = 0;
263
22.0k
  Byte Byte = 0x00;
264
  // Check whether reading exceeds the data or section boundary.
265
22.0k
  EXPECTED_TRY(testRead(8));
266
198k
  for (uint32_t I = 0; I < 8; I++) {
267
176k
    Byte = Data[Pos++];
268
176k
    Buf |= (Byte & UINT64_C(0xFF)) << (I * UINT64_C(8));
269
176k
  }
270
22.0k
  double Result;
271
22.0k
  static_assert(sizeof(Buf) == sizeof(Result));
272
22.0k
  std::memcpy(&Result, &Buf, sizeof(Result));
273
22.0k
  return Result;
274
22.0k
}
275
276
// Read a vector of bytes. See "include/loader/filemgr.h".
277
1.57M
Expect<std::string> FileMgr::readName() {
278
1.57M
  if (unlikely(Status != ErrCode::Value::Success)) {
279
0
    return Unexpect(Status);
280
0
  }
281
  // If UTF-8 validation, readU32(), or readBytes() failed, the last successful
282
  // reading offset will be at the start of `Name`.
283
1.57M
  LastPos = Pos;
284
285
  // Read the name size.
286
1.57M
  EXPECTED_TRY(uint32_t SizeToRead, readU32());
287
288
  // Check whether the string length exceeds the data boundary.
289
1.57M
  if (auto Res = testRead(SizeToRead); unlikely(!Res)) {
290
232
    return Unexpect(ErrCode::Value::LengthOutOfBounds);
291
232
  }
292
293
  // Read the UTF-8 bytes.
294
1.57M
  std::string Str(SizeToRead, '\0');
295
1.57M
  EXPECTED_TRY(
296
1.57M
      readBytes(Span<Byte>(reinterpret_cast<Byte *>(Str.data()), Str.size())));
297
298
  // UTF-8 validation.
299
1.57M
  bool Valid = true;
300
2.75M
  for (uint32_t I = 0; I < Str.size() && Valid; ++I) {
301
1.17M
    char C = Str.data()[I];
302
1.17M
    uint32_t N = 0;
303
1.17M
    if ((C & '\x80') == 0) {
304
      // 0xxxxxxx, 7 bits UCS, ASCII
305
1.16M
      N = 0;
306
1.16M
    } else if ((C & '\xE0') == '\xC0') {
307
      // 110xxxxx, 11 bits UCS, U+80 to U+7FF
308
3.07k
      N = 1;
309
14.0k
    } else if ((C & '\xF0') == '\xE0') {
310
      // 1110xxxx, 16 bits UCS, U+800 to U+D7FF and U+E000 to U+FFFF
311
10.3k
      N = 2;
312
10.3k
    } else if ((C & '\xF8') == '\xF0') {
313
      // 11110xxx, 21 bits UCS, U+10000 to U+10FFFF
314
3.62k
      N = 3;
315
3.62k
    } else {
316
150
      Valid = false;
317
150
    }
318
319
    // Need N more bytes.
320
1.17M
    if (I + N >= Str.size()) {
321
39
      Valid = false;
322
39
    }
323
    // Invalid ranges
324
1.17M
    if (N == 1 && (C & '\xDE') == '\xC0') {
325
      // 11 bits UCS, U+0 to U+80, FAIL
326
7
      Valid = false;
327
1.17M
    } else if (N == 2 &&
328
10.3k
               ((C == '\xE0' && (Str.data()[I + 1] & '\xA0') == '\x80') ||
329
                // 16 bits UCS, U+0 to U+7FF, FAIL
330
10.3k
                (C == '\xED' && (Str.data()[I + 1] & '\xA0') == '\xA0')
331
                // 16 bits UCS, U+D800 to U+DFFF, FAIL
332
10.3k
                )) {
333
8
      Valid = false;
334
1.17M
    } else if (N == 3 &&
335
3.62k
               ((C == '\xF0' && (Str.data()[I + 1] & '\xB0') == '\x80') ||
336
                // 21 bits UCS, U+0 to U+FFFF, FAIL
337
3.61k
                (C == '\xF4' && (Str.data()[I + 1] & '\xB0') != '\x80') ||
338
                // 21 bits UCS, U+110000 to U+13FFFF, FAIL
339
3.60k
                (C != '\xF4' && (C & '\xF4') == '\xF4')
340
                // 21 bits UCS, U+140000 to U+1FFFFF, FAIL
341
3.62k
                )) {
342
24
      Valid = false;
343
24
    }
344
345
1.21M
    for (uint32_t J = 0; J < N && Valid; ++J) {
346
      // N bytes need to match 10xxxxxx
347
34.3k
      if ((Str.data()[I + J + 1] & '\xC0') != '\x80') {
348
87
        Valid = false;
349
87
      }
350
34.3k
    }
351
1.17M
    I += N;
352
1.17M
  }
353
354
1.57M
  if (!Valid) {
355
306
    Status = ErrCode::Value::MalformedUTF8;
356
306
    return Unexpect(Status);
357
306
  }
358
1.57M
  return Str;
359
1.57M
}
360
361
// Peek one byte. See "include/loader/filemgr.h".
362
1.20M
Expect<Byte> FileMgr::peekByte() {
363
1.20M
  EXPECTED_TRY(Byte B, readByte());
364
1.20M
  Pos--;
365
1.20M
  return B;
366
1.20M
}
367
368
// Get the file header type. See "include/loader/filemgr.h".
369
23.4k
FileMgr::FileHeader FileMgr::getHeaderType() {
370
23.4k
  if (Size >= 4) {
371
23.3k
    Byte WASMMagic[] = {0x00, 0x61, 0x73, 0x6D};
372
23.3k
    Byte ELFMagic[] = {0x7F, 0x45, 0x4C, 0x46};
373
23.3k
    Byte MAC32agic[] = {0xCE, 0xFA, 0xED, 0xFE};
374
23.3k
    Byte MAC64agic[] = {0xCF, 0xFA, 0xED, 0xFE};
375
23.3k
    if (std::equal(WASMMagic, WASMMagic + 4, Data)) {
376
23.2k
      return FileMgr::FileHeader::Wasm;
377
23.2k
    } else if (std::equal(ELFMagic, ELFMagic + 4, Data)) {
378
3
      return FileMgr::FileHeader::ELF;
379
90
    } else if (std::equal(MAC32agic, MAC32agic + 4, Data)) {
380
2
      return FileMgr::FileHeader::MachO_32;
381
88
    } else if (std::equal(MAC64agic, MAC64agic + 4, Data)) {
382
2
      return FileMgr::FileHeader::MachO_64;
383
2
    }
384
23.3k
  }
385
170
  if (Size >= 2) {
386
154
    Byte DLLMagic[] = {0x4D, 0x5A};
387
154
    if (std::equal(DLLMagic, DLLMagic + 2, Data)) {
388
3
      return FileMgr::FileHeader::DLL;
389
3
    }
390
154
  }
391
167
  return FileMgr::FileHeader::Unknown;
392
170
}
393
394
// Jump a section. See "include/loader/filemgr.h".
395
0
Expect<void> FileMgr::jumpContent() {
396
0
  if (unlikely(Status != ErrCode::Value::Success)) {
397
0
    return Unexpect(Status);
398
0
  }
399
  // Set the flag to the start offset.
400
0
  LastPos = Pos;
401
  // Read the section size.
402
0
  EXPECTED_TRY(uint32_t SecSize, readU32());
403
  // Jump the content.
404
0
  EXPECTED_TRY(testRead(SecSize));
405
0
  Pos += SecSize;
406
0
  return {};
407
0
}
408
409
// Helper function for reading bytes. See "include/loader/filemgr.h".
410
3.04M
Expect<void> FileMgr::readBytes(Span<Byte> Buffer) {
411
3.04M
  if (unlikely(Status != ErrCode::Value::Success)) {
412
0
    return Unexpect(Status);
413
0
  }
414
  // The adjustment of `LastPos` should be handled by the caller.
415
3.04M
  auto SizeToRead = Buffer.size();
416
3.04M
  if (likely(SizeToRead > 0)) {
417
    // Check whether reading exceeds the data boundary.
418
298k
    EXPECTED_TRY(testRead(SizeToRead));
419
298k
    std::copy_n(Data + Pos, SizeToRead, Buffer.begin());
420
298k
    Pos += SizeToRead;
421
298k
  }
422
3.04M
  return {};
423
3.04M
}
424
425
// Helper function for checking boundary. See "include/loader/filemgr.h".
426
55.1M
Expect<void> FileMgr::testRead(uint64_t Read) {
427
  // Check whether reading exceeds the data boundary.
428
55.1M
  if (unlikely(getRemainSize() < Read)) {
429
16.8k
    Pos = Size;
430
16.8k
    LastPos = Pos;
431
16.8k
    Status = ErrCode::Value::UnexpectedEnd;
432
16.8k
    return Unexpect(Status);
433
16.8k
  }
434
55.1M
  return {};
435
55.1M
}
436
437
} // namespace WasmEdge