1//===-- DataExtractor.cpp -------------------------------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "llvm/Support/DataExtractor.h"
11#include "llvm/Support/ErrorHandling.h"
12#include "llvm/Support/Host.h"
13#include "llvm/Support/SwapByteOrder.h"
14using namespace llvm;
15
16template <typename T>
17static T getU(uint32_t *offset_ptr, const DataExtractor *de,
18              bool isLittleEndian, const char *Data) {
19  T val = 0;
20  uint32_t offset = *offset_ptr;
21  if (de->isValidOffsetForDataOfSize(offset, sizeof(val))) {
22    std::memcpy(&val, &Data[offset], sizeof(val));
23    if (sys::isLittleEndianHost() != isLittleEndian)
24      val = sys::SwapByteOrder(val);
25
26    // Advance the offset
27    *offset_ptr += sizeof(val);
28  }
29  return val;
30}
31
32template <typename T>
33static T *getUs(uint32_t *offset_ptr, T *dst, uint32_t count,
34                const DataExtractor *de, bool isLittleEndian, const char *Data){
35  uint32_t offset = *offset_ptr;
36
37  if (count > 0 && de->isValidOffsetForDataOfSize(offset, sizeof(*dst)*count)) {
38    for (T *value_ptr = dst, *end = dst + count; value_ptr != end;
39        ++value_ptr, offset += sizeof(*dst))
40      *value_ptr = getU<T>(offset_ptr, de, isLittleEndian, Data);
41    // Advance the offset
42    *offset_ptr = offset;
43    // Return a non-NULL pointer to the converted data as an indicator of
44    // success
45    return dst;
46  }
47  return NULL;
48}
49
50uint8_t DataExtractor::getU8(uint32_t *offset_ptr) const {
51  return getU<uint8_t>(offset_ptr, this, IsLittleEndian, Data.data());
52}
53
54uint8_t *
55DataExtractor::getU8(uint32_t *offset_ptr, uint8_t *dst, uint32_t count) const {
56  return getUs<uint8_t>(offset_ptr, dst, count, this, IsLittleEndian,
57                       Data.data());
58}
59
60
61uint16_t DataExtractor::getU16(uint32_t *offset_ptr) const {
62  return getU<uint16_t>(offset_ptr, this, IsLittleEndian, Data.data());
63}
64
65uint16_t *DataExtractor::getU16(uint32_t *offset_ptr, uint16_t *dst,
66                                uint32_t count) const {
67  return getUs<uint16_t>(offset_ptr, dst, count, this, IsLittleEndian,
68                        Data.data());
69}
70
71uint32_t DataExtractor::getU32(uint32_t *offset_ptr) const {
72  return getU<uint32_t>(offset_ptr, this, IsLittleEndian, Data.data());
73}
74
75uint32_t *DataExtractor::getU32(uint32_t *offset_ptr, uint32_t *dst,
76                                uint32_t count) const {
77  return getUs<uint32_t>(offset_ptr, dst, count, this, IsLittleEndian,
78                        Data.data());
79}
80
81uint64_t DataExtractor::getU64(uint32_t *offset_ptr) const {
82  return getU<uint64_t>(offset_ptr, this, IsLittleEndian, Data.data());
83}
84
85uint64_t *DataExtractor::getU64(uint32_t *offset_ptr, uint64_t *dst,
86                                uint32_t count) const {
87  return getUs<uint64_t>(offset_ptr, dst, count, this, IsLittleEndian,
88                        Data.data());
89}
90
91uint64_t
92DataExtractor::getUnsigned(uint32_t *offset_ptr, uint32_t byte_size) const {
93  switch (byte_size) {
94  case 1:
95    return getU8(offset_ptr);
96  case 2:
97    return getU16(offset_ptr);
98  case 4:
99    return getU32(offset_ptr);
100  case 8:
101    return getU64(offset_ptr);
102  }
103  llvm_unreachable("getUnsigned unhandled case!");
104}
105
106int64_t
107DataExtractor::getSigned(uint32_t *offset_ptr, uint32_t byte_size) const {
108  switch (byte_size) {
109  case 1:
110    return (int8_t)getU8(offset_ptr);
111  case 2:
112    return (int16_t)getU16(offset_ptr);
113  case 4:
114    return (int32_t)getU32(offset_ptr);
115  case 8:
116    return (int64_t)getU64(offset_ptr);
117  }
118  llvm_unreachable("getSigned unhandled case!");
119}
120
121const char *DataExtractor::getCStr(uint32_t *offset_ptr) const {
122  uint32_t offset = *offset_ptr;
123  StringRef::size_type pos = Data.find('\0', offset);
124  if (pos != StringRef::npos) {
125    *offset_ptr = pos + 1;
126    return Data.data() + offset;
127  }
128  return NULL;
129}
130
131uint64_t DataExtractor::getULEB128(uint32_t *offset_ptr) const {
132  uint64_t result = 0;
133  if (Data.empty())
134    return 0;
135
136  unsigned shift = 0;
137  uint32_t offset = *offset_ptr;
138  uint8_t byte = 0;
139
140  while (isValidOffset(offset)) {
141    byte = Data[offset++];
142    result |= uint64_t(byte & 0x7f) << shift;
143    shift += 7;
144    if ((byte & 0x80) == 0)
145      break;
146  }
147
148  *offset_ptr = offset;
149  return result;
150}
151
152int64_t DataExtractor::getSLEB128(uint32_t *offset_ptr) const {
153  int64_t result = 0;
154  if (Data.empty())
155    return 0;
156
157  unsigned shift = 0;
158  uint32_t offset = *offset_ptr;
159  uint8_t byte = 0;
160
161  while (isValidOffset(offset)) {
162    byte = Data[offset++];
163    result |= uint64_t(byte & 0x7f) << shift;
164    shift += 7;
165    if ((byte & 0x80) == 0)
166      break;
167  }
168
169  // Sign bit of byte is 2nd high order bit (0x40)
170  if (shift < 64 && (byte & 0x40))
171    result |= -(1ULL << shift);
172
173  *offset_ptr = offset;
174  return result;
175}
176