Coverage Report

Created: 2026-11-09 06:49

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
31.9k
Expect<void> FileMgr::setCode(Span<const Byte> CodeData) {
49
31.9k
  reset();
50
31.9k
  Data = CodeData.data();
51
31.9k
  Size = CodeData.size();
52
31.9k
  Status = ErrCode::Value::Success;
53
31.9k
  return {};
54
31.9k
}
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
71.9M
Expect<Byte> FileMgr::readByte() {
73
71.9M
  if (unlikely(Status != ErrCode::Value::Success)) {
74
0
    return Unexpect(Status);
75
0
  }
76
  // Set the flag to the start offset.
77
71.9M
  LastPos = Pos;
78
  // Check whether reading exceeds the data or section boundary.
79
71.9M
  EXPECTED_TRY(testRead(1));
80
71.8M
  return Data[Pos++];
81
71.9M
}
82
83
// Read bytes. See "include/loader/filemgr.h".
84
6.12M
Expect<std::vector<Byte>> FileMgr::readBytes(size_t SizeToRead) {
85
6.12M
  if (unlikely(Status != ErrCode::Value::Success)) {
86
0
    return Unexpect(Status);
87
0
  }
88
  // Set the flag to the start offset.
89
6.12M
  LastPos = Pos;
90
  // Check whether reading exceeds the data boundary before allocating.
91
6.12M
  EXPECTED_TRY(testRead(SizeToRead));
92
  // Read bytes into vector.
93
6.12M
  std::vector<Byte> Buf(SizeToRead);
94
6.12M
  EXPECTED_TRY(readBytes(Buf));
95
6.12M
  return Buf;
96
6.12M
}
97
98
// Decode and read an unsigned int. See "include/loader/filemgr.h".
99
46.5M
Expect<uint32_t> FileMgr::readU32() {
100
46.5M
  if (unlikely(Status != ErrCode::Value::Success)) {
101
0
    return Unexpect(Status);
102
0
  }
103
  // Set the flag to the start offset.
104
46.5M
  LastPos = Pos;
105
106
  // Read and decode U32.
107
46.5M
  uint32_t Result = 0;
108
46.5M
  uint32_t Offset = 0;
109
46.5M
  Byte Byte = 0x80;
110
95.1M
  while (Byte & 0x80) {
111
48.6M
    if (unlikely(Offset >= 32)) {
112
19
      Status = ErrCode::Value::IntegerTooLong;
113
19
      return Unexpect(Status);
114
19
    }
115
48.6M
    EXPECTED_TRY(testRead(1));
116
48.6M
    Byte = Data[Pos++];
117
48.6M
    Result |= (Byte & UINT32_C(0x7F)) << Offset;
118
48.6M
    if (Offset == 28 && unlikely((Byte & UINT32_C(0x70)) != 0)) {
119
196
      Status = ErrCode::Value::IntegerTooLarge;
120
196
      return Unexpect(Status);
121
196
    }
122
48.6M
    Offset += 7;
123
48.6M
  }
124
46.5M
  return Result;
125
46.5M
}
126
127
// Decode and read an unsigned long long int. See "include/loader/filemgr.h".
128
708k
Expect<uint64_t> FileMgr::readU64() {
129
708k
  if (Status != ErrCode::Value::Success) {
130
0
    return Unexpect(Status);
131
0
  }
132
  // Set the flag to the start offset.
133
708k
  LastPos = Pos;
134
135
  // Read and decode U64.
136
708k
  uint64_t Result = 0;
137
708k
  uint64_t Offset = 0;
138
708k
  Byte Byte = 0x80;
139
1.50M
  while (Byte & 0x80) {
140
800k
    if (unlikely(Offset >= 64)) {
141
4
      Status = ErrCode::Value::IntegerTooLong;
142
4
      return Unexpect(Status);
143
4
    }
144
800k
    EXPECTED_TRY(testRead(1));
145
799k
    Byte = Data[Pos++];
146
799k
    Result |= (Byte & UINT64_C(0x7F)) << Offset;
147
799k
    if (Offset == 63 && unlikely((Byte & UINT32_C(0x7E)) != 0)) {
148
12
      Status = ErrCode::Value::IntegerTooLarge;
149
12
      return Unexpect(Status);
150
12
    }
151
799k
    Offset += 7;
152
799k
  }
153
708k
  return Result;
154
708k
}
155
156
10.2M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
157
10.2M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
158
10.2M
  static_assert(8 * sizeof(RetType) >= N,
159
10.2M
                "RetType cannot hold the full range of S_N");
160
10.2M
  static_assert(std::is_signed_v<RetType>,
161
10.2M
                "RetType of S_N must be signed type");
162
163
10.2M
  if (Status != ErrCode::Value::Success) {
164
0
    return Unexpect(Status);
165
0
  }
166
  // Set the flag to the start offset.
167
10.2M
  LastPos = Pos;
168
169
  // Read and decode S_N.
170
10.2M
  RetType Result = 0;
171
10.2M
  size_t Offset = 0;
172
10.2M
  size_t RemainingBits = N;
173
11.2M
  while (true) {
174
11.2M
    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
11.2M
    EXPECTED_TRY(testRead(1));
181
11.2M
    WasmEdge::Byte Byte = Data[Pos++];
182
183
11.2M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
184
11.2M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
185
11.2M
    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
111
        Status = ErrCode::Value::IntegerTooLong;
190
111
        return Unexpect(Status);
191
111
      }
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
10.2M
    } 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
10.2M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
205
10.2M
      std::make_unsigned_t<RetType> Payload = Byte;
206
10.2M
      if (Byte & SecondHighestBitMask) {
207
        // The byte is signed.
208
7.21M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
209
7.21M
          Payload -= (1 << 7);
210
7.21M
        } else {
211
29
          Status = ErrCode::Value::IntegerTooLarge;
212
29
          return Unexpect(Status);
213
29
        }
214
7.21M
      } else {
215
        // The byte is unsigned.
216
3.02M
        if (Byte >= (1 << (EffectiveBits - 1))) {
217
22
          Status = ErrCode::Value::IntegerTooLarge;
218
22
          return Unexpect(Status);
219
22
        }
220
3.02M
      }
221
10.2M
      Result |= (Payload << Offset);
222
10.2M
      break;
223
10.2M
    }
224
11.2M
  }
225
226
10.2M
  return Result;
227
10.2M
}
cxx20::expected<long, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<long, 33ul>()
Line
Count
Source
156
7.26M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
157
7.26M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
158
7.26M
  static_assert(8 * sizeof(RetType) >= N,
159
7.26M
                "RetType cannot hold the full range of S_N");
160
7.26M
  static_assert(std::is_signed_v<RetType>,
161
7.26M
                "RetType of S_N must be signed type");
162
163
7.26M
  if (Status != ErrCode::Value::Success) {
164
0
    return Unexpect(Status);
165
0
  }
166
  // Set the flag to the start offset.
167
7.26M
  LastPos = Pos;
168
169
  // Read and decode S_N.
170
7.26M
  RetType Result = 0;
171
7.26M
  size_t Offset = 0;
172
7.26M
  size_t RemainingBits = N;
173
7.33M
  while (true) {
174
7.33M
    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
7.33M
    EXPECTED_TRY(testRead(1));
181
7.33M
    WasmEdge::Byte Byte = Data[Pos++];
182
183
7.33M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
184
7.33M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
185
7.33M
    if (Byte & HighestBitMask) {
186
      // The byte has a leading 1. It contains a 7-bit payload.
187
188
75.1k
      if (unlikely(RemainingBits < 7)) {
189
67
        Status = ErrCode::Value::IntegerTooLong;
190
67
        return Unexpect(Status);
191
67
      }
192
193
75.0k
      std::make_unsigned_t<RetType> Payload =
194
75.0k
          Byte & (~HighestBitMask); // & 0b01111111
195
75.0k
      Result |= (Payload << Offset);
196
75.0k
      Offset += 7;
197
75.0k
      RemainingBits -= 7;
198
7.26M
    } 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
7.26M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
205
7.26M
      std::make_unsigned_t<RetType> Payload = Byte;
206
7.26M
      if (Byte & SecondHighestBitMask) {
207
        // The byte is signed.
208
4.90M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
209
4.90M
          Payload -= (1 << 7);
210
4.90M
        } else {
211
8
          Status = ErrCode::Value::IntegerTooLarge;
212
8
          return Unexpect(Status);
213
8
        }
214
4.90M
      } else {
215
        // The byte is unsigned.
216
2.36M
        if (Byte >= (1 << (EffectiveBits - 1))) {
217
5
          Status = ErrCode::Value::IntegerTooLarge;
218
5
          return Unexpect(Status);
219
5
        }
220
2.36M
      }
221
7.26M
      Result |= (Payload << Offset);
222
7.26M
      break;
223
7.26M
    }
224
7.33M
  }
225
226
7.26M
  return Result;
227
7.26M
}
cxx20::expected<int, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<int, 32ul>()
Line
Count
Source
156
2.34M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
157
2.34M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
158
2.34M
  static_assert(8 * sizeof(RetType) >= N,
159
2.34M
                "RetType cannot hold the full range of S_N");
160
2.34M
  static_assert(std::is_signed_v<RetType>,
161
2.34M
                "RetType of S_N must be signed type");
162
163
2.34M
  if (Status != ErrCode::Value::Success) {
164
0
    return Unexpect(Status);
165
0
  }
166
  // Set the flag to the start offset.
167
2.34M
  LastPos = Pos;
168
169
  // Read and decode S_N.
170
2.34M
  RetType Result = 0;
171
2.34M
  size_t Offset = 0;
172
2.34M
  size_t RemainingBits = N;
173
2.66M
  while (true) {
174
2.66M
    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.66M
    EXPECTED_TRY(testRead(1));
181
2.66M
    WasmEdge::Byte Byte = Data[Pos++];
182
183
2.66M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
184
2.66M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
185
2.66M
    if (Byte & HighestBitMask) {
186
      // The byte has a leading 1. It contains a 7-bit payload.
187
188
321k
      if (unlikely(RemainingBits < 7)) {
189
14
        Status = ErrCode::Value::IntegerTooLong;
190
14
        return Unexpect(Status);
191
14
      }
192
193
321k
      std::make_unsigned_t<RetType> Payload =
194
321k
          Byte & (~HighestBitMask); // & 0b01111111
195
321k
      Result |= (Payload << Offset);
196
321k
      Offset += 7;
197
321k
      RemainingBits -= 7;
198
2.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
2.34M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
205
2.34M
      std::make_unsigned_t<RetType> Payload = Byte;
206
2.34M
      if (Byte & SecondHighestBitMask) {
207
        // The byte is signed.
208
1.85M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
209
1.85M
          Payload -= (1 << 7);
210
1.85M
        } else {
211
8
          Status = ErrCode::Value::IntegerTooLarge;
212
8
          return Unexpect(Status);
213
8
        }
214
1.85M
      } else {
215
        // The byte is unsigned.
216
494k
        if (Byte >= (1 << (EffectiveBits - 1))) {
217
8
          Status = ErrCode::Value::IntegerTooLarge;
218
8
          return Unexpect(Status);
219
8
        }
220
494k
      }
221
2.34M
      Result |= (Payload << Offset);
222
2.34M
      break;
223
2.34M
    }
224
2.66M
  }
225
226
2.34M
  return Result;
227
2.34M
}
cxx20::expected<long, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<long, 64ul>()
Line
Count
Source
156
634k
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
157
634k
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
158
634k
  static_assert(8 * sizeof(RetType) >= N,
159
634k
                "RetType cannot hold the full range of S_N");
160
634k
  static_assert(std::is_signed_v<RetType>,
161
634k
                "RetType of S_N must be signed type");
162
163
634k
  if (Status != ErrCode::Value::Success) {
164
0
    return Unexpect(Status);
165
0
  }
166
  // Set the flag to the start offset.
167
634k
  LastPos = Pos;
168
169
  // Read and decode S_N.
170
634k
  RetType Result = 0;
171
634k
  size_t Offset = 0;
172
634k
  size_t RemainingBits = N;
173
1.27M
  while (true) {
174
1.27M
    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.27M
    EXPECTED_TRY(testRead(1));
181
1.27M
    WasmEdge::Byte Byte = Data[Pos++];
182
183
1.27M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
184
1.27M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
185
1.27M
    if (Byte & HighestBitMask) {
186
      // The byte has a leading 1. It contains a 7-bit payload.
187
188
638k
      if (unlikely(RemainingBits < 7)) {
189
30
        Status = ErrCode::Value::IntegerTooLong;
190
30
        return Unexpect(Status);
191
30
      }
192
193
638k
      std::make_unsigned_t<RetType> Payload =
194
638k
          Byte & (~HighestBitMask); // & 0b01111111
195
638k
      Result |= (Payload << Offset);
196
638k
      Offset += 7;
197
638k
      RemainingBits -= 7;
198
638k
    } 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
634k
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
205
634k
      std::make_unsigned_t<RetType> Payload = Byte;
206
634k
      if (Byte & SecondHighestBitMask) {
207
        // The byte is signed.
208
462k
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
209
462k
          Payload -= (1 << 7);
210
462k
        } else {
211
13
          Status = ErrCode::Value::IntegerTooLarge;
212
13
          return Unexpect(Status);
213
13
        }
214
462k
      } else {
215
        // The byte is unsigned.
216
172k
        if (Byte >= (1 << (EffectiveBits - 1))) {
217
9
          Status = ErrCode::Value::IntegerTooLarge;
218
9
          return Unexpect(Status);
219
9
        }
220
172k
      }
221
634k
      Result |= (Payload << Offset);
222
634k
      break;
223
634k
    }
224
1.27M
  }
225
226
634k
  return Result;
227
634k
}
228
229
7.26M
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.34M
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
634k
Expect<int64_t> FileMgr::readS64() { return readSN<int64_t, 64>(); }
236
237
// Copy bytes to a float. See "include/loader/filemgr.h".
238
67.4k
Expect<float> FileMgr::readF32() {
239
67.4k
  if (Status != ErrCode::Value::Success) {
240
0
    return Unexpect(Status);
241
0
  }
242
  // Set the flag to the start offset.
243
67.4k
  LastPos = Pos;
244
245
67.4k
  uint32_t Buf = 0;
246
67.4k
  Byte Byte = 0x00;
247
  // Check whether reading exceeds the data or section boundary.
248
67.4k
  EXPECTED_TRY(testRead(4));
249
337k
  for (uint32_t I = 0; I < 4; I++) {
250
269k
    Byte = Data[Pos++];
251
269k
    Buf |= (Byte & UINT32_C(0xFF)) << (I * UINT32_C(8));
252
269k
  }
253
67.4k
  float Result;
254
67.4k
  static_assert(sizeof(Buf) == sizeof(Result));
255
67.4k
  std::memcpy(&Result, &Buf, sizeof(Result));
256
67.4k
  return Result;
257
67.4k
}
258
259
// Copy bytes to a double. See "include/loader/filemgr.h".
260
56.8k
Expect<double> FileMgr::readF64() {
261
56.8k
  if (Status != ErrCode::Value::Success) {
262
0
    return Unexpect(Status);
263
0
  }
264
  // Set the flag to the start offset.
265
56.8k
  LastPos = Pos;
266
267
56.8k
  uint64_t Buf = 0;
268
56.8k
  Byte Byte = 0x00;
269
  // Check whether reading exceeds the data or section boundary.
270
56.8k
  EXPECTED_TRY(testRead(8));
271
511k
  for (uint32_t I = 0; I < 8; I++) {
272
454k
    Byte = Data[Pos++];
273
454k
    Buf |= (Byte & UINT64_C(0xFF)) << (I * UINT64_C(8));
274
454k
  }
275
56.8k
  double Result;
276
56.8k
  static_assert(sizeof(Buf) == sizeof(Result));
277
56.8k
  std::memcpy(&Result, &Buf, sizeof(Result));
278
56.8k
  return Result;
279
56.8k
}
280
281
// Read a vector of bytes. See "include/loader/filemgr.h".
282
6.73M
Expect<std::string> FileMgr::readName() {
283
6.73M
  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
6.73M
  LastPos = Pos;
289
290
  // Read the name size.
291
6.73M
  EXPECTED_TRY(uint32_t SizeToRead, readU32());
292
293
  // Check whether the string length exceeds the data boundary.
294
6.73M
  if (auto Res = testRead(SizeToRead); unlikely(!Res)) {
295
256
    return Unexpect(ErrCode::Value::LengthOutOfBounds);
296
256
  }
297
298
  // Read the UTF-8 bytes.
299
6.73M
  std::string Str(SizeToRead, '\0');
300
6.73M
  EXPECTED_TRY(
301
6.73M
      readBytes(Span<Byte>(reinterpret_cast<Byte *>(Str.data()), Str.size())));
302
303
  // UTF-8 validation.
304
6.73M
  bool Valid = true;
305
10.4M
  for (uint32_t I = 0; I < Str.size() && Valid; ++I) {
306
3.73M
    char C = Str.data()[I];
307
3.73M
    uint32_t N = 0;
308
3.73M
    if ((C & '\x80') == 0) {
309
      // 0xxxxxxx, 7 bits UCS, ASCII
310
3.72M
      N = 0;
311
3.72M
    } else if ((C & '\xE0') == '\xC0') {
312
      // 110xxxxx, 11 bits UCS, U+80 to U+7FF
313
2.56k
      N = 1;
314
10.4k
    } else if ((C & '\xF0') == '\xE0') {
315
      // 1110xxxx, 16 bits UCS, U+800 to U+D7FF and U+E000 to U+FFFF
316
6.68k
      N = 2;
317
6.68k
    } else if ((C & '\xF8') == '\xF0') {
318
      // 11110xxx, 21 bits UCS, U+10000 to U+10FFFF
319
3.54k
      N = 3;
320
3.54k
    } else {
321
177
      Valid = false;
322
177
    }
323
324
    // Need N more bytes.
325
3.73M
    if (I + N >= Str.size()) {
326
45
      Valid = false;
327
45
    }
328
    // Invalid ranges
329
3.73M
    if (N == 1 && (C & '\xDE') == '\xC0') {
330
      // 11 bits UCS, U+0 to U+80, FAIL
331
6
      Valid = false;
332
3.73M
    } else if (N == 2 &&
333
6.68k
               ((C == '\xE0' && (Str.data()[I + 1] & '\xA0') == '\x80') ||
334
                // 16 bits UCS, U+0 to U+7FF, FAIL
335
6.68k
                (C == '\xED' && (Str.data()[I + 1] & '\xA0') == '\xA0')
336
                // 16 bits UCS, U+D800 to U+DFFF, FAIL
337
6.68k
                )) {
338
12
      Valid = false;
339
3.73M
    } else if (N == 3 &&
340
3.54k
               ((C == '\xF0' && (Str.data()[I + 1] & '\xB0') == '\x80') ||
341
                // 21 bits UCS, U+0 to U+FFFF, FAIL
342
3.53k
                (C == '\xF4' && (Str.data()[I + 1] & '\xB0') != '\x80') ||
343
                // 21 bits UCS, U+110000 to U+13FFFF, FAIL
344
3.52k
                (C != '\xF4' && (C & '\xF4') == '\xF4')
345
                // 21 bits UCS, U+140000 to U+1FFFFF, FAIL
346
3.54k
                )) {
347
24
      Valid = false;
348
24
    }
349
350
3.76M
    for (uint32_t J = 0; J < N && Valid; ++J) {
351
      // N bytes need to match 10xxxxxx
352
26.3k
      if ((Str.data()[I + J + 1] & '\xC0') != '\x80') {
353
122
        Valid = false;
354
122
      }
355
26.3k
    }
356
3.73M
    I += N;
357
3.73M
  }
358
359
6.73M
  if (!Valid) {
360
378
    Status = ErrCode::Value::MalformedUTF8;
361
378
    return Unexpect(Status);
362
378
  }
363
6.73M
  return Str;
364
6.73M
}
365
366
// Peek one byte. See "include/loader/filemgr.h".
367
2.50M
Expect<Byte> FileMgr::peekByte() {
368
2.50M
  EXPECTED_TRY(Byte B, readByte());
369
2.50M
  Pos--;
370
2.50M
  return B;
371
2.50M
}
372
373
// Get the file header type. See "include/loader/filemgr.h".
374
31.9k
FileMgr::FileHeader FileMgr::getHeaderType() {
375
31.9k
  if (Size >= 4) {
376
31.8k
    Byte WASMMagic[] = {0x00, 0x61, 0x73, 0x6D};
377
31.8k
    Byte ELFMagic[] = {0x7F, 0x45, 0x4C, 0x46};
378
31.8k
    Byte MAC32agic[] = {0xCE, 0xFA, 0xED, 0xFE};
379
31.8k
    Byte MAC64agic[] = {0xCF, 0xFA, 0xED, 0xFE};
380
31.8k
    if (std::equal(WASMMagic, WASMMagic + 4, Data)) {
381
31.7k
      return FileMgr::FileHeader::Wasm;
382
31.7k
    } else if (std::equal(ELFMagic, ELFMagic + 4, Data)) {
383
3
      return FileMgr::FileHeader::ELF;
384
78
    } else if (std::equal(MAC32agic, MAC32agic + 4, Data)) {
385
2
      return FileMgr::FileHeader::MachO_32;
386
76
    } else if (std::equal(MAC64agic, MAC64agic + 4, Data)) {
387
2
      return FileMgr::FileHeader::MachO_64;
388
2
    }
389
31.8k
  }
390
173
  if (Size >= 2) {
391
152
    Byte DLLMagic[] = {0x4D, 0x5A};
392
152
    if (std::equal(DLLMagic, DLLMagic + 2, Data)) {
393
5
      return FileMgr::FileHeader::DLL;
394
5
    }
395
152
  }
396
168
  return FileMgr::FileHeader::Unknown;
397
173
}
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
12.8M
Expect<void> FileMgr::readBytes(Span<Byte> Buffer) {
416
12.8M
  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
12.8M
  auto SizeToRead = Buffer.size();
421
12.8M
  if (likely(SizeToRead > 0)) {
422
    // Check whether reading exceeds the data boundary.
423
1.65M
    EXPECTED_TRY(testRead(SizeToRead));
424
1.65M
    std::copy_n(Data + Pos, SizeToRead, Buffer.begin());
425
1.65M
    Pos += SizeToRead;
426
1.65M
  }
427
12.8M
  return {};
428
12.8M
}
429
430
// Helper function for checking boundary. See "include/loader/filemgr.h".
431
147M
Expect<void> FileMgr::testRead(uint64_t Read) {
432
  // Check whether reading exceeds the data boundary.
433
147M
  if (unlikely(getRemainSize() < Read)) {
434
24.0k
    Pos = Size;
435
24.0k
    LastPos = Pos;
436
24.0k
    Status = ErrCode::Value::UnexpectedEnd;
437
24.0k
    return Unexpect(Status);
438
24.0k
  }
439
147M
  return {};
440
147M
}
441
442
} // namespace WasmEdge