1226584Sdim//===-- DataExtractor.cpp -------------------------------------------------===//
2226584Sdim//
3226584Sdim//                     The LLVM Compiler Infrastructure
4226584Sdim//
5226584Sdim// This file is distributed under the University of Illinois Open Source
6226584Sdim// License. See LICENSE.TXT for details.
7226584Sdim//
8226584Sdim//===----------------------------------------------------------------------===//
9226584Sdim
10226584Sdim#include "llvm/Support/DataExtractor.h"
11226584Sdim#include "llvm/Support/ErrorHandling.h"
12226584Sdim#include "llvm/Support/Host.h"
13226584Sdim#include "llvm/Support/SwapByteOrder.h"
14226584Sdimusing namespace llvm;
15226584Sdim
16226584Sdimtemplate <typename T>
17226584Sdimstatic T getU(uint32_t *offset_ptr, const DataExtractor *de,
18226584Sdim              bool isLittleEndian, const char *Data) {
19226584Sdim  T val = 0;
20226584Sdim  uint32_t offset = *offset_ptr;
21226584Sdim  if (de->isValidOffsetForDataOfSize(offset, sizeof(val))) {
22226584Sdim    std::memcpy(&val, &Data[offset], sizeof(val));
23251662Sdim    if (sys::IsLittleEndianHost != isLittleEndian)
24226584Sdim      val = sys::SwapByteOrder(val);
25226584Sdim
26226584Sdim    // Advance the offset
27226584Sdim    *offset_ptr += sizeof(val);
28226584Sdim  }
29226584Sdim  return val;
30226584Sdim}
31226584Sdim
32226584Sdimtemplate <typename T>
33226584Sdimstatic T *getUs(uint32_t *offset_ptr, T *dst, uint32_t count,
34226584Sdim                const DataExtractor *de, bool isLittleEndian, const char *Data){
35226584Sdim  uint32_t offset = *offset_ptr;
36226584Sdim
37226584Sdim  if (count > 0 && de->isValidOffsetForDataOfSize(offset, sizeof(*dst)*count)) {
38226584Sdim    for (T *value_ptr = dst, *end = dst + count; value_ptr != end;
39226584Sdim        ++value_ptr, offset += sizeof(*dst))
40226584Sdim      *value_ptr = getU<T>(offset_ptr, de, isLittleEndian, Data);
41226584Sdim    // Advance the offset
42226584Sdim    *offset_ptr = offset;
43226584Sdim    // Return a non-NULL pointer to the converted data as an indicator of
44226584Sdim    // success
45226584Sdim    return dst;
46226584Sdim  }
47226584Sdim  return NULL;
48226584Sdim}
49226584Sdim
50226584Sdimuint8_t DataExtractor::getU8(uint32_t *offset_ptr) const {
51226584Sdim  return getU<uint8_t>(offset_ptr, this, IsLittleEndian, Data.data());
52226584Sdim}
53226584Sdim
54226584Sdimuint8_t *
55226584SdimDataExtractor::getU8(uint32_t *offset_ptr, uint8_t *dst, uint32_t count) const {
56226584Sdim  return getUs<uint8_t>(offset_ptr, dst, count, this, IsLittleEndian,
57226584Sdim                       Data.data());
58226584Sdim}
59226584Sdim
60226584Sdim
61226584Sdimuint16_t DataExtractor::getU16(uint32_t *offset_ptr) const {
62226584Sdim  return getU<uint16_t>(offset_ptr, this, IsLittleEndian, Data.data());
63226584Sdim}
64226584Sdim
65226584Sdimuint16_t *DataExtractor::getU16(uint32_t *offset_ptr, uint16_t *dst,
66226584Sdim                                uint32_t count) const {
67226584Sdim  return getUs<uint16_t>(offset_ptr, dst, count, this, IsLittleEndian,
68226584Sdim                        Data.data());
69226584Sdim}
70226584Sdim
71226584Sdimuint32_t DataExtractor::getU32(uint32_t *offset_ptr) const {
72226584Sdim  return getU<uint32_t>(offset_ptr, this, IsLittleEndian, Data.data());
73226584Sdim}
74226584Sdim
75226584Sdimuint32_t *DataExtractor::getU32(uint32_t *offset_ptr, uint32_t *dst,
76226584Sdim                                uint32_t count) const {
77226584Sdim  return getUs<uint32_t>(offset_ptr, dst, count, this, IsLittleEndian,
78234353Sdim                        Data.data());
79226584Sdim}
80226584Sdim
81226584Sdimuint64_t DataExtractor::getU64(uint32_t *offset_ptr) const {
82226584Sdim  return getU<uint64_t>(offset_ptr, this, IsLittleEndian, Data.data());
83226584Sdim}
84226584Sdim
85226584Sdimuint64_t *DataExtractor::getU64(uint32_t *offset_ptr, uint64_t *dst,
86226584Sdim                                uint32_t count) const {
87226584Sdim  return getUs<uint64_t>(offset_ptr, dst, count, this, IsLittleEndian,
88226584Sdim                        Data.data());
89226584Sdim}
90226584Sdim
91226584Sdimuint64_t
92226584SdimDataExtractor::getUnsigned(uint32_t *offset_ptr, uint32_t byte_size) const {
93226584Sdim  switch (byte_size) {
94226584Sdim  case 1:
95226584Sdim    return getU8(offset_ptr);
96226584Sdim  case 2:
97226584Sdim    return getU16(offset_ptr);
98226584Sdim  case 4:
99226584Sdim    return getU32(offset_ptr);
100226584Sdim  case 8:
101226584Sdim    return getU64(offset_ptr);
102226584Sdim  }
103226584Sdim  llvm_unreachable("getUnsigned unhandled case!");
104226584Sdim}
105226584Sdim
106226584Sdimint64_t
107226584SdimDataExtractor::getSigned(uint32_t *offset_ptr, uint32_t byte_size) const {
108226584Sdim  switch (byte_size) {
109226584Sdim  case 1:
110226584Sdim    return (int8_t)getU8(offset_ptr);
111226584Sdim  case 2:
112226584Sdim    return (int16_t)getU16(offset_ptr);
113226584Sdim  case 4:
114226584Sdim    return (int32_t)getU32(offset_ptr);
115226584Sdim  case 8:
116226584Sdim    return (int64_t)getU64(offset_ptr);
117226584Sdim  }
118226584Sdim  llvm_unreachable("getSigned unhandled case!");
119226584Sdim}
120226584Sdim
121226584Sdimconst char *DataExtractor::getCStr(uint32_t *offset_ptr) const {
122226584Sdim  uint32_t offset = *offset_ptr;
123226584Sdim  StringRef::size_type pos = Data.find('\0', offset);
124226584Sdim  if (pos != StringRef::npos) {
125226584Sdim    *offset_ptr = pos + 1;
126226584Sdim    return Data.data() + offset;
127226584Sdim  }
128226584Sdim  return NULL;
129226584Sdim}
130226584Sdim
131226584Sdimuint64_t DataExtractor::getULEB128(uint32_t *offset_ptr) const {
132226584Sdim  uint64_t result = 0;
133226584Sdim  if (Data.empty())
134226584Sdim    return 0;
135226584Sdim
136226584Sdim  unsigned shift = 0;
137226584Sdim  uint32_t offset = *offset_ptr;
138226584Sdim  uint8_t byte = 0;
139226584Sdim
140226584Sdim  while (isValidOffset(offset)) {
141226584Sdim    byte = Data[offset++];
142243830Sdim    result |= uint64_t(byte & 0x7f) << shift;
143226584Sdim    shift += 7;
144226584Sdim    if ((byte & 0x80) == 0)
145226584Sdim      break;
146226584Sdim  }
147226584Sdim
148226584Sdim  *offset_ptr = offset;
149226584Sdim  return result;
150226584Sdim}
151226584Sdim
152226584Sdimint64_t DataExtractor::getSLEB128(uint32_t *offset_ptr) const {
153226584Sdim  int64_t result = 0;
154226584Sdim  if (Data.empty())
155226584Sdim    return 0;
156226584Sdim
157226584Sdim  unsigned shift = 0;
158226584Sdim  uint32_t offset = *offset_ptr;
159226584Sdim  uint8_t byte = 0;
160226584Sdim
161226584Sdim  while (isValidOffset(offset)) {
162226584Sdim    byte = Data[offset++];
163243830Sdim    result |= uint64_t(byte & 0x7f) << shift;
164226584Sdim    shift += 7;
165226584Sdim    if ((byte & 0x80) == 0)
166226584Sdim      break;
167226584Sdim  }
168226584Sdim
169226584Sdim  // Sign bit of byte is 2nd high order bit (0x40)
170226584Sdim  if (shift < 64 && (byte & 0x40))
171243830Sdim    result |= -(1ULL << shift);
172226584Sdim
173226584Sdim  *offset_ptr = offset;
174226584Sdim  return result;
175226584Sdim}
176