xref: /freebsd/contrib/llvm-project/llvm/lib/Support/DataExtractor.cpp (revision 480093f4440d54b30b3025afeac24b48f2ba7a2e)
1 //===-- DataExtractor.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/Support/DataExtractor.h"
10 #include "llvm/Support/Errc.h"
11 #include "llvm/Support/ErrorHandling.h"
12 #include "llvm/Support/Host.h"
13 #include "llvm/Support/LEB128.h"
14 #include "llvm/Support/SwapByteOrder.h"
15 
16 using namespace llvm;
17 
18 static void unexpectedEndReached(Error *E) {
19   if (E)
20     *E = createStringError(errc::illegal_byte_sequence,
21                            "unexpected end of data");
22 }
23 
24 static bool isError(Error *E) { return E && *E; }
25 
26 template <typename T>
27 static T getU(uint64_t *offset_ptr, const DataExtractor *de,
28               bool isLittleEndian, const char *Data, llvm::Error *Err) {
29   ErrorAsOutParameter ErrAsOut(Err);
30   T val = 0;
31   if (isError(Err))
32     return val;
33 
34   uint64_t offset = *offset_ptr;
35   if (!de->isValidOffsetForDataOfSize(offset, sizeof(T))) {
36     unexpectedEndReached(Err);
37     return val;
38   }
39   std::memcpy(&val, &Data[offset], sizeof(val));
40   if (sys::IsLittleEndianHost != isLittleEndian)
41     sys::swapByteOrder(val);
42 
43   // Advance the offset
44   *offset_ptr += sizeof(val);
45   return val;
46 }
47 
48 template <typename T>
49 static T *getUs(uint64_t *offset_ptr, T *dst, uint32_t count,
50                 const DataExtractor *de, bool isLittleEndian, const char *Data,
51                 llvm::Error *Err) {
52   ErrorAsOutParameter ErrAsOut(Err);
53   if (isError(Err))
54     return nullptr;
55 
56   uint64_t offset = *offset_ptr;
57 
58   if (!de->isValidOffsetForDataOfSize(offset, sizeof(*dst) * count)) {
59     unexpectedEndReached(Err);
60     return nullptr;
61   }
62   for (T *value_ptr = dst, *end = dst + count; value_ptr != end;
63        ++value_ptr, offset += sizeof(*dst))
64     *value_ptr = getU<T>(offset_ptr, de, isLittleEndian, Data, Err);
65   // Advance the offset
66   *offset_ptr = offset;
67   // Return a non-NULL pointer to the converted data as an indicator of
68   // success
69   return dst;
70 }
71 
72 uint8_t DataExtractor::getU8(uint64_t *offset_ptr, llvm::Error *Err) const {
73   return getU<uint8_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
74 }
75 
76 uint8_t *
77 DataExtractor::getU8(uint64_t *offset_ptr, uint8_t *dst, uint32_t count) const {
78   return getUs<uint8_t>(offset_ptr, dst, count, this, IsLittleEndian,
79                         Data.data(), nullptr);
80 }
81 
82 uint8_t *DataExtractor::getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const {
83   return getUs<uint8_t>(&C.Offset, Dst, Count, this, IsLittleEndian,
84                         Data.data(), &C.Err);
85 }
86 
87 uint16_t DataExtractor::getU16(uint64_t *offset_ptr, llvm::Error *Err) const {
88   return getU<uint16_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
89 }
90 
91 uint16_t *DataExtractor::getU16(uint64_t *offset_ptr, uint16_t *dst,
92                                 uint32_t count) const {
93   return getUs<uint16_t>(offset_ptr, dst, count, this, IsLittleEndian,
94                          Data.data(), nullptr);
95 }
96 
97 uint32_t DataExtractor::getU24(uint64_t *offset_ptr) const {
98   uint24_t ExtractedVal =
99       getU<uint24_t>(offset_ptr, this, IsLittleEndian, Data.data(), nullptr);
100   // The 3 bytes are in the correct byte order for the host.
101   return ExtractedVal.getAsUint32(sys::IsLittleEndianHost);
102 }
103 
104 uint32_t DataExtractor::getU32(uint64_t *offset_ptr, llvm::Error *Err) const {
105   return getU<uint32_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
106 }
107 
108 uint32_t *DataExtractor::getU32(uint64_t *offset_ptr, uint32_t *dst,
109                                 uint32_t count) const {
110   return getUs<uint32_t>(offset_ptr, dst, count, this, IsLittleEndian,
111                          Data.data(), nullptr);
112 }
113 
114 uint64_t DataExtractor::getU64(uint64_t *offset_ptr, llvm::Error *Err) const {
115   return getU<uint64_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err);
116 }
117 
118 uint64_t *DataExtractor::getU64(uint64_t *offset_ptr, uint64_t *dst,
119                                 uint32_t count) const {
120   return getUs<uint64_t>(offset_ptr, dst, count, this, IsLittleEndian,
121                          Data.data(), nullptr);
122 }
123 
124 uint64_t DataExtractor::getUnsigned(uint64_t *offset_ptr, uint32_t byte_size,
125                                     llvm::Error *Err) const {
126   switch (byte_size) {
127   case 1:
128     return getU8(offset_ptr, Err);
129   case 2:
130     return getU16(offset_ptr, Err);
131   case 4:
132     return getU32(offset_ptr, Err);
133   case 8:
134     return getU64(offset_ptr, Err);
135   }
136   llvm_unreachable("getUnsigned unhandled case!");
137 }
138 
139 int64_t
140 DataExtractor::getSigned(uint64_t *offset_ptr, uint32_t byte_size) const {
141   switch (byte_size) {
142   case 1:
143     return (int8_t)getU8(offset_ptr);
144   case 2:
145     return (int16_t)getU16(offset_ptr);
146   case 4:
147     return (int32_t)getU32(offset_ptr);
148   case 8:
149     return (int64_t)getU64(offset_ptr);
150   }
151   llvm_unreachable("getSigned unhandled case!");
152 }
153 
154 const char *DataExtractor::getCStr(uint64_t *offset_ptr) const {
155   uint64_t offset = *offset_ptr;
156   StringRef::size_type pos = Data.find('\0', offset);
157   if (pos != StringRef::npos) {
158     *offset_ptr = pos + 1;
159     return Data.data() + offset;
160   }
161   return nullptr;
162 }
163 
164 StringRef DataExtractor::getCStrRef(uint64_t *offset_ptr) const {
165   uint64_t Start = *offset_ptr;
166   StringRef::size_type Pos = Data.find('\0', Start);
167   if (Pos != StringRef::npos) {
168     *offset_ptr = Pos + 1;
169     return StringRef(Data.data() + Start, Pos - Start);
170   }
171   return StringRef();
172 }
173 
174 uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr,
175                                    llvm::Error *Err) const {
176   assert(*offset_ptr <= Data.size());
177   ErrorAsOutParameter ErrAsOut(Err);
178   if (isError(Err))
179     return 0;
180 
181   const char *error;
182   unsigned bytes_read;
183   uint64_t result = decodeULEB128(
184       reinterpret_cast<const uint8_t *>(Data.data() + *offset_ptr), &bytes_read,
185       reinterpret_cast<const uint8_t *>(Data.data() + Data.size()), &error);
186   if (error) {
187     if (Err)
188       *Err = createStringError(errc::illegal_byte_sequence, error);
189     return 0;
190   }
191   *offset_ptr += bytes_read;
192   return result;
193 }
194 
195 int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr) const {
196   assert(*offset_ptr <= Data.size());
197 
198   const char *error;
199   unsigned bytes_read;
200   int64_t result = decodeSLEB128(
201       reinterpret_cast<const uint8_t *>(Data.data() + *offset_ptr), &bytes_read,
202       reinterpret_cast<const uint8_t *>(Data.data() + Data.size()), &error);
203   if (error)
204     return 0;
205   *offset_ptr += bytes_read;
206   return result;
207 }
208 
209 void DataExtractor::skip(Cursor &C, uint64_t Length) const {
210   ErrorAsOutParameter ErrAsOut(&C.Err);
211   if (isError(&C.Err))
212     return;
213 
214   if (isValidOffsetForDataOfSize(C.Offset, Length))
215     C.Offset += Length;
216   else
217     unexpectedEndReached(&C.Err);
218 }
219