Coverage Report

Created: 2026-08-08 06:32

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
28.8k
Expect<void> FileMgr::setCode(Span<const Byte> CodeData) {
49
28.8k
  reset();
50
28.8k
  Data = CodeData.data();
51
28.8k
  Size = CodeData.size();
52
28.8k
  Status = ErrCode::Value::Success;
53
28.8k
  return {};
54
28.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
55.9M
Expect<Byte> FileMgr::readByte() {
73
55.9M
  if (unlikely(Status != ErrCode::Value::Success)) {
74
0
    return Unexpect(Status);
75
0
  }
76
  // Set the flag to the start offset.
77
55.9M
  LastPos = Pos;
78
  // Check whether reading exceeds the data or section boundary.
79
55.9M
  EXPECTED_TRY(testRead(1));
80
55.9M
  return Data[Pos++];
81
55.9M
}
82
83
// Read bytes. See "include/loader/filemgr.h".
84
3.88M
Expect<std::vector<Byte>> FileMgr::readBytes(size_t SizeToRead) {
85
  // Set the flag to the start offset.
86
3.88M
  LastPos = Pos;
87
  // Read bytes into vector.
88
3.88M
  std::vector<Byte> Buf(SizeToRead);
89
3.88M
  EXPECTED_TRY(readBytes(Buf));
90
3.88M
  return Buf;
91
3.88M
}
92
93
// Decode and read an unsigned int. See "include/loader/filemgr.h".
94
27.9M
Expect<uint32_t> FileMgr::readU32() {
95
27.9M
  if (unlikely(Status != ErrCode::Value::Success)) {
96
0
    return Unexpect(Status);
97
0
  }
98
  // Set the flag to the start offset.
99
27.9M
  LastPos = Pos;
100
101
  // Read and decode U32.
102
27.9M
  uint32_t Result = 0;
103
27.9M
  uint32_t Offset = 0;
104
27.9M
  Byte Byte = 0x80;
105
56.8M
  while (Byte & 0x80) {
106
28.9M
    if (unlikely(Offset >= 32)) {
107
27
      Status = ErrCode::Value::IntegerTooLong;
108
27
      return Unexpect(Status);
109
27
    }
110
28.9M
    EXPECTED_TRY(testRead(1));
111
28.9M
    Byte = Data[Pos++];
112
28.9M
    Result |= (Byte & UINT32_C(0x7F)) << Offset;
113
28.9M
    if (Offset == 28 && unlikely((Byte & UINT32_C(0x70)) != 0)) {
114
199
      Status = ErrCode::Value::IntegerTooLarge;
115
199
      return Unexpect(Status);
116
199
    }
117
28.9M
    Offset += 7;
118
28.9M
  }
119
27.9M
  return Result;
120
27.9M
}
121
122
// Decode and read an unsigned long long int. See "include/loader/filemgr.h".
123
752k
Expect<uint64_t> FileMgr::readU64() {
124
752k
  if (Status != ErrCode::Value::Success) {
125
0
    return Unexpect(Status);
126
0
  }
127
  // Set the flag to the start offset.
128
752k
  LastPos = Pos;
129
130
  // Read and decode U64.
131
752k
  uint64_t Result = 0;
132
752k
  uint64_t Offset = 0;
133
752k
  Byte Byte = 0x80;
134
1.56M
  while (Byte & 0x80) {
135
815k
    if (unlikely(Offset >= 64)) {
136
2
      Status = ErrCode::Value::IntegerTooLong;
137
2
      return Unexpect(Status);
138
2
    }
139
815k
    EXPECTED_TRY(testRead(1));
140
815k
    Byte = Data[Pos++];
141
815k
    Result |= (Byte & UINT64_C(0x7F)) << Offset;
142
815k
    if (Offset == 63 && unlikely((Byte & UINT32_C(0x7E)) != 0)) {
143
9
      Status = ErrCode::Value::IntegerTooLarge;
144
9
      return Unexpect(Status);
145
9
    }
146
815k
    Offset += 7;
147
815k
  }
148
751k
  return Result;
149
752k
}
150
151
5.89M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
152
5.89M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
153
5.89M
  static_assert(8 * sizeof(RetType) >= N,
154
5.89M
                "RetType cannot hold the full range of S_N");
155
5.89M
  static_assert(std::is_signed_v<RetType>,
156
5.89M
                "RetType of S_N must be signed type");
157
158
5.89M
  if (Status != ErrCode::Value::Success) {
159
0
    return Unexpect(Status);
160
0
  }
161
  // Set the flag to the start offset.
162
5.89M
  LastPos = Pos;
163
164
  // Read and decode S_N.
165
5.89M
  RetType Result = 0;
166
5.89M
  size_t Offset = 0;
167
5.89M
  size_t RemainingBits = N;
168
7.72M
  while (true) {
169
7.72M
    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
7.72M
    EXPECTED_TRY(testRead(1));
176
7.72M
    WasmEdge::Byte Byte = Data[Pos++];
177
178
7.72M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
179
7.72M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
180
7.72M
    if (Byte & HighestBitMask) {
181
      // The byte has a leading 1. It contains a 7-bit payload.
182
183
1.82M
      if (unlikely(RemainingBits < 7)) {
184
117
        Status = ErrCode::Value::IntegerTooLong;
185
117
        return Unexpect(Status);
186
117
      }
187
188
1.82M
      std::make_unsigned_t<RetType> Payload =
189
1.82M
          Byte & (~HighestBitMask); // & 0b01111111
190
1.82M
      Result |= (Payload << Offset);
191
1.82M
      Offset += 7;
192
1.82M
      RemainingBits -= 7;
193
5.89M
    } 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
5.89M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
200
5.89M
      std::make_unsigned_t<RetType> Payload = Byte;
201
5.89M
      if (Byte & SecondHighestBitMask) {
202
        // The byte is signed.
203
3.59M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
204
3.59M
          Payload -= (1 << 7);
205
3.59M
        } else {
206
39
          Status = ErrCode::Value::IntegerTooLarge;
207
39
          return Unexpect(Status);
208
39
        }
209
3.59M
      } else {
210
        // The byte is unsigned.
211
2.30M
        if (Byte >= (1 << (EffectiveBits - 1))) {
212
28
          Status = ErrCode::Value::IntegerTooLarge;
213
28
          return Unexpect(Status);
214
28
        }
215
2.30M
      }
216
5.89M
      Result |= (Payload << Offset);
217
5.89M
      break;
218
5.89M
    }
219
7.72M
  }
220
221
5.89M
  return Result;
222
5.89M
}
cxx20::expected<long, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<long, 33ul>()
Line
Count
Source
151
1.93M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
152
1.93M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
153
1.93M
  static_assert(8 * sizeof(RetType) >= N,
154
1.93M
                "RetType cannot hold the full range of S_N");
155
1.93M
  static_assert(std::is_signed_v<RetType>,
156
1.93M
                "RetType of S_N must be signed type");
157
158
1.93M
  if (Status != ErrCode::Value::Success) {
159
0
    return Unexpect(Status);
160
0
  }
161
  // Set the flag to the start offset.
162
1.93M
  LastPos = Pos;
163
164
  // Read and decode S_N.
165
1.93M
  RetType Result = 0;
166
1.93M
  size_t Offset = 0;
167
1.93M
  size_t RemainingBits = N;
168
2.03M
  while (true) {
169
2.03M
    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.03M
    EXPECTED_TRY(testRead(1));
176
2.03M
    WasmEdge::Byte Byte = Data[Pos++];
177
178
2.03M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
179
2.03M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
180
2.03M
    if (Byte & HighestBitMask) {
181
      // The byte has a leading 1. It contains a 7-bit payload.
182
183
94.7k
      if (unlikely(RemainingBits < 7)) {
184
39
        Status = ErrCode::Value::IntegerTooLong;
185
39
        return Unexpect(Status);
186
39
      }
187
188
94.7k
      std::make_unsigned_t<RetType> Payload =
189
94.7k
          Byte & (~HighestBitMask); // & 0b01111111
190
94.7k
      Result |= (Payload << Offset);
191
94.7k
      Offset += 7;
192
94.7k
      RemainingBits -= 7;
193
1.93M
    } 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.93M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
200
1.93M
      std::make_unsigned_t<RetType> Payload = Byte;
201
1.93M
      if (Byte & SecondHighestBitMask) {
202
        // The byte is signed.
203
568k
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
204
568k
          Payload -= (1 << 7);
205
568k
        } else {
206
10
          Status = ErrCode::Value::IntegerTooLarge;
207
10
          return Unexpect(Status);
208
10
        }
209
1.36M
      } else {
210
        // The byte is unsigned.
211
1.36M
        if (Byte >= (1 << (EffectiveBits - 1))) {
212
5
          Status = ErrCode::Value::IntegerTooLarge;
213
5
          return Unexpect(Status);
214
5
        }
215
1.36M
      }
216
1.93M
      Result |= (Payload << Offset);
217
1.93M
      break;
218
1.93M
    }
219
2.03M
  }
220
221
1.93M
  return Result;
222
1.93M
}
cxx20::expected<int, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<int, 32ul>()
Line
Count
Source
151
2.49M
template <typename RetType, size_t N> Expect<RetType> FileMgr::readSN() {
152
2.49M
  static_assert(N >= 8, "The N of S_N must have at least length of a byte");
153
2.49M
  static_assert(8 * sizeof(RetType) >= N,
154
2.49M
                "RetType cannot hold the full range of S_N");
155
2.49M
  static_assert(std::is_signed_v<RetType>,
156
2.49M
                "RetType of S_N must be signed type");
157
158
2.49M
  if (Status != ErrCode::Value::Success) {
159
0
    return Unexpect(Status);
160
0
  }
161
  // Set the flag to the start offset.
162
2.49M
  LastPos = Pos;
163
164
  // Read and decode S_N.
165
2.49M
  RetType Result = 0;
166
2.49M
  size_t Offset = 0;
167
2.49M
  size_t RemainingBits = N;
168
2.71M
  while (true) {
169
2.71M
    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.71M
    EXPECTED_TRY(testRead(1));
176
2.71M
    WasmEdge::Byte Byte = Data[Pos++];
177
178
2.71M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
179
2.71M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
180
2.71M
    if (Byte & HighestBitMask) {
181
      // The byte has a leading 1. It contains a 7-bit payload.
182
183
224k
      if (unlikely(RemainingBits < 7)) {
184
23
        Status = ErrCode::Value::IntegerTooLong;
185
23
        return Unexpect(Status);
186
23
      }
187
188
224k
      std::make_unsigned_t<RetType> Payload =
189
224k
          Byte & (~HighestBitMask); // & 0b01111111
190
224k
      Result |= (Payload << Offset);
191
224k
      Offset += 7;
192
224k
      RemainingBits -= 7;
193
2.49M
    } 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.49M
      size_t EffectiveBits = RemainingBits < 7 ? RemainingBits : 7;
200
2.49M
      std::make_unsigned_t<RetType> Payload = Byte;
201
2.49M
      if (Byte & SecondHighestBitMask) {
202
        // The byte is signed.
203
1.96M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
204
1.96M
          Payload -= (1 << 7);
205
1.96M
        } else {
206
8
          Status = ErrCode::Value::IntegerTooLarge;
207
8
          return Unexpect(Status);
208
8
        }
209
1.96M
      } else {
210
        // The byte is unsigned.
211
530k
        if (Byte >= (1 << (EffectiveBits - 1))) {
212
9
          Status = ErrCode::Value::IntegerTooLarge;
213
9
          return Unexpect(Status);
214
9
        }
215
530k
      }
216
2.49M
      Result |= (Payload << Offset);
217
2.49M
      break;
218
2.49M
    }
219
2.71M
  }
220
221
2.49M
  return Result;
222
2.49M
}
cxx20::expected<long, WasmEdge::ErrCode> WasmEdge::FileMgr::readSN<long, 64ul>()
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
2.96M
  while (true) {
169
2.96M
    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.96M
    EXPECTED_TRY(testRead(1));
176
2.96M
    WasmEdge::Byte Byte = Data[Pos++];
177
178
2.96M
    const WasmEdge::Byte HighestBitMask = 1 << 7;
179
2.96M
    const WasmEdge::Byte SecondHighestBitMask = 1 << 6;
180
2.96M
    if (Byte & HighestBitMask) {
181
      // The byte has a leading 1. It contains a 7-bit payload.
182
183
1.50M
      if (unlikely(RemainingBits < 7)) {
184
55
        Status = ErrCode::Value::IntegerTooLong;
185
55
        return Unexpect(Status);
186
55
      }
187
188
1.50M
      std::make_unsigned_t<RetType> Payload =
189
1.50M
          Byte & (~HighestBitMask); // & 0b01111111
190
1.50M
      Result |= (Payload << Offset);
191
1.50M
      Offset += 7;
192
1.50M
      RemainingBits -= 7;
193
1.50M
    } 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
1.05M
        if (Byte >= (1 << 7) - (1 << (EffectiveBits - 1))) {
204
1.05M
          Payload -= (1 << 7);
205
1.05M
        } else {
206
21
          Status = ErrCode::Value::IntegerTooLarge;
207
21
          return Unexpect(Status);
208
21
        }
209
1.05M
      } else {
210
        // The byte is unsigned.
211
403k
        if (Byte >= (1 << (EffectiveBits - 1))) {
212
14
          Status = ErrCode::Value::IntegerTooLarge;
213
14
          return Unexpect(Status);
214
14
        }
215
403k
      }
216
1.46M
      Result |= (Payload << Offset);
217
1.46M
      break;
218
1.46M
    }
219
2.96M
  }
220
221
1.46M
  return Result;
222
1.46M
}
223
224
1.93M
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.49M
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
1.46M
Expect<int64_t> FileMgr::readS64() { return readSN<int64_t, 64>(); }
231
232
// Copy bytes to a float. See "include/loader/filemgr.h".
233
45.2k
Expect<float> FileMgr::readF32() {
234
45.2k
  if (Status != ErrCode::Value::Success) {
235
0
    return Unexpect(Status);
236
0
  }
237
  // Set the flag to the start offset.
238
45.2k
  LastPos = Pos;
239
240
45.2k
  uint32_t Buf = 0;
241
45.2k
  Byte Byte = 0x00;
242
  // Check whether reading exceeds the data or section boundary.
243
45.2k
  EXPECTED_TRY(testRead(4));
244
226k
  for (uint32_t I = 0; I < 4; I++) {
245
180k
    Byte = Data[Pos++];
246
180k
    Buf |= (Byte & UINT32_C(0xFF)) << (I * UINT32_C(8));
247
180k
  }
248
45.2k
  float Result;
249
45.2k
  static_assert(sizeof(Buf) == sizeof(Result));
250
45.2k
  std::memcpy(&Result, &Buf, sizeof(Result));
251
45.2k
  return Result;
252
45.2k
}
253
254
// Copy bytes to a double. See "include/loader/filemgr.h".
255
26.9k
Expect<double> FileMgr::readF64() {
256
26.9k
  if (Status != ErrCode::Value::Success) {
257
0
    return Unexpect(Status);
258
0
  }
259
  // Set the flag to the start offset.
260
26.9k
  LastPos = Pos;
261
262
26.9k
  uint64_t Buf = 0;
263
26.9k
  Byte Byte = 0x00;
264
  // Check whether reading exceeds the data or section boundary.
265
26.9k
  EXPECTED_TRY(testRead(8));
266
241k
  for (uint32_t I = 0; I < 8; I++) {
267
215k
    Byte = Data[Pos++];
268
215k
    Buf |= (Byte & UINT64_C(0xFF)) << (I * UINT64_C(8));
269
215k
  }
270
26.8k
  double Result;
271
26.8k
  static_assert(sizeof(Buf) == sizeof(Result));
272
26.8k
  std::memcpy(&Result, &Buf, sizeof(Result));
273
26.8k
  return Result;
274
26.9k
}
275
276
// Read a vector of bytes. See "include/loader/filemgr.h".
277
4.04M
Expect<std::string> FileMgr::readName() {
278
4.04M
  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
4.04M
  LastPos = Pos;
284
285
  // Read the name size.
286
4.04M
  EXPECTED_TRY(uint32_t SizeToRead, readU32());
287
288
  // Check whether the string length exceeds the data boundary.
289
4.04M
  if (auto Res = testRead(SizeToRead); unlikely(!Res)) {
290
269
    return Unexpect(ErrCode::Value::LengthOutOfBounds);
291
269
  }
292
293
  // Read the UTF-8 bytes.
294
4.04M
  std::string Str(SizeToRead, '\0');
295
4.04M
  EXPECTED_TRY(
296
4.04M
      readBytes(Span<Byte>(reinterpret_cast<Byte *>(Str.data()), Str.size())));
297
298
  // UTF-8 validation.
299
4.04M
  bool Valid = true;
300
6.00M
  for (uint32_t I = 0; I < Str.size() && Valid; ++I) {
301
1.96M
    char C = Str.data()[I];
302
1.96M
    uint32_t N = 0;
303
1.96M
    if ((C & '\x80') == 0) {
304
      // 0xxxxxxx, 7 bits UCS, ASCII
305
1.94M
      N = 0;
306
1.94M
    } else if ((C & '\xE0') == '\xC0') {
307
      // 110xxxxx, 11 bits UCS, U+80 to U+7FF
308
2.79k
      N = 1;
309
12.9k
    } else if ((C & '\xF0') == '\xE0') {
310
      // 1110xxxx, 16 bits UCS, U+800 to U+D7FF and U+E000 to U+FFFF
311
9.31k
      N = 2;
312
9.31k
    } else if ((C & '\xF8') == '\xF0') {
313
      // 11110xxx, 21 bits UCS, U+10000 to U+10FFFF
314
3.45k
      N = 3;
315
3.45k
    } else {
316
202
      Valid = false;
317
202
    }
318
319
    // Need N more bytes.
320
1.96M
    if (I + N >= Str.size()) {
321
45
      Valid = false;
322
45
    }
323
    // Invalid ranges
324
1.96M
    if (N == 1 && (C & '\xDE') == '\xC0') {
325
      // 11 bits UCS, U+0 to U+80, FAIL
326
9
      Valid = false;
327
1.96M
    } else if (N == 2 &&
328
9.31k
               ((C == '\xE0' && (Str.data()[I + 1] & '\xA0') == '\x80') ||
329
                // 16 bits UCS, U+0 to U+7FF, FAIL
330
9.31k
                (C == '\xED' && (Str.data()[I + 1] & '\xA0') == '\xA0')
331
                // 16 bits UCS, U+D800 to U+DFFF, FAIL
332
9.31k
                )) {
333
12
      Valid = false;
334
1.96M
    } else if (N == 3 &&
335
3.45k
               ((C == '\xF0' && (Str.data()[I + 1] & '\xB0') == '\x80') ||
336
                // 21 bits UCS, U+0 to U+FFFF, FAIL
337
3.45k
                (C == '\xF4' && (Str.data()[I + 1] & '\xB0') != '\x80') ||
338
                // 21 bits UCS, U+110000 to U+13FFFF, FAIL
339
3.43k
                (C != '\xF4' && (C & '\xF4') == '\xF4')
340
                // 21 bits UCS, U+140000 to U+1FFFFF, FAIL
341
3.45k
                )) {
342
30
      Valid = false;
343
30
    }
344
345
1.99M
    for (uint32_t J = 0; J < N && Valid; ++J) {
346
      // N bytes need to match 10xxxxxx
347
31.5k
      if ((Str.data()[I + J + 1] & '\xC0') != '\x80') {
348
97
        Valid = false;
349
97
      }
350
31.5k
    }
351
1.96M
    I += N;
352
1.96M
  }
353
354
4.04M
  if (!Valid) {
355
386
    Status = ErrCode::Value::MalformedUTF8;
356
386
    return Unexpect(Status);
357
386
  }
358
4.04M
  return Str;
359
4.04M
}
360
361
// Peek one byte. See "include/loader/filemgr.h".
362
1.67M
Expect<Byte> FileMgr::peekByte() {
363
1.67M
  EXPECTED_TRY(Byte B, readByte());
364
1.67M
  Pos--;
365
1.67M
  return B;
366
1.67M
}
367
368
// Get the file header type. See "include/loader/filemgr.h".
369
28.8k
FileMgr::FileHeader FileMgr::getHeaderType() {
370
28.8k
  if (Size >= 4) {
371
28.7k
    Byte WASMMagic[] = {0x00, 0x61, 0x73, 0x6D};
372
28.7k
    Byte ELFMagic[] = {0x7F, 0x45, 0x4C, 0x46};
373
28.7k
    Byte MAC32agic[] = {0xCE, 0xFA, 0xED, 0xFE};
374
28.7k
    Byte MAC64agic[] = {0xCF, 0xFA, 0xED, 0xFE};
375
28.7k
    if (std::equal(WASMMagic, WASMMagic + 4, Data)) {
376
28.6k
      return FileMgr::FileHeader::Wasm;
377
28.6k
    } else if (std::equal(ELFMagic, ELFMagic + 4, Data)) {
378
2
      return FileMgr::FileHeader::ELF;
379
91
    } else if (std::equal(MAC32agic, MAC32agic + 4, Data)) {
380
3
      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
28.7k
  }
385
176
  if (Size >= 2) {
386
158
    Byte DLLMagic[] = {0x4D, 0x5A};
387
158
    if (std::equal(DLLMagic, DLLMagic + 2, Data)) {
388
6
      return FileMgr::FileHeader::DLL;
389
6
    }
390
158
  }
391
170
  return FileMgr::FileHeader::Unknown;
392
176
}
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
7.92M
Expect<void> FileMgr::readBytes(Span<Byte> Buffer) {
411
7.92M
  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
7.92M
  auto SizeToRead = Buffer.size();
416
7.92M
  if (likely(SizeToRead > 0)) {
417
    // Check whether reading exceeds the data boundary.
418
518k
    EXPECTED_TRY(testRead(SizeToRead));
419
518k
    std::copy_n(Data + Pos, SizeToRead, Buffer.begin());
420
518k
    Pos += SizeToRead;
421
518k
  }
422
7.92M
  return {};
423
7.92M
}
424
425
// Helper function for checking boundary. See "include/loader/filemgr.h".
426
98.1M
Expect<void> FileMgr::testRead(uint64_t Read) {
427
  // Check whether reading exceeds the data boundary.
428
98.1M
  if (unlikely(getRemainSize() < Read)) {
429
21.1k
    Pos = Size;
430
21.1k
    LastPos = Pos;
431
21.1k
    Status = ErrCode::Value::UnexpectedEnd;
432
21.1k
    return Unexpect(Status);
433
21.1k
  }
434
98.1M
  return {};
435
98.1M
}
436
437
} // namespace WasmEdge