225 lines
5.5 KiB
C++
225 lines
5.5 KiB
C++
//===- LEB128.cpp ---------------------------------------------------------===//
|
|
//
|
|
// The MCLinker Project
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "mcld/Support/LEB128.h"
|
|
|
|
namespace mcld {
|
|
|
|
namespace leb128 {
|
|
|
|
//===---------------------- LEB128 Encoding APIs -------------------------===//
|
|
template <>
|
|
size_t encode<uint64_t>(ByteType*& pBuf, uint64_t pValue) {
|
|
size_t size = 0;
|
|
do {
|
|
ByteType byte = pValue & 0x7f;
|
|
pValue >>= 7;
|
|
if (pValue)
|
|
byte |= 0x80;
|
|
*pBuf++ = byte;
|
|
size++;
|
|
} while (pValue);
|
|
|
|
return size;
|
|
}
|
|
|
|
/*
|
|
* Fast version for encoding 32-bit integer. This unrolls the loop in the
|
|
* generic version defined above.
|
|
*/
|
|
template <>
|
|
size_t encode<uint32_t>(ByteType*& pBuf, uint32_t pValue) {
|
|
if ((pValue & ~0x7f) == 0) {
|
|
*pBuf++ = static_cast<ByteType>(pValue);
|
|
return 1;
|
|
} else if ((pValue & ~0x3fff) == 0) {
|
|
*pBuf++ = static_cast<ByteType>((pValue & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>((pValue >> 7) & 0x7f);
|
|
return 2;
|
|
} else if ((pValue & ~0x1fffff) == 0) {
|
|
*pBuf++ = static_cast<ByteType>((pValue & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>(((pValue >> 7) & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>((pValue >> 14) & 0x7f);
|
|
return 3;
|
|
} else if ((pValue & ~0xfffffff) == 0) {
|
|
*pBuf++ = static_cast<ByteType>((pValue & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>(((pValue >> 7) & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>(((pValue >> 14) & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>((pValue >> 21) & 0x7f);
|
|
return 4;
|
|
} else {
|
|
*pBuf++ = static_cast<ByteType>((pValue & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>(((pValue >> 7) & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>(((pValue >> 14) & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>(((pValue >> 21) & 0x7f) | 0x80);
|
|
*pBuf++ = static_cast<ByteType>((pValue >> 28) & 0x7f);
|
|
return 5;
|
|
}
|
|
// unreachable
|
|
}
|
|
|
|
template <>
|
|
size_t encode<int64_t>(ByteType*& pBuf, int64_t pValue) {
|
|
size_t size = 0;
|
|
bool more = true;
|
|
|
|
do {
|
|
ByteType byte = pValue & 0x7f;
|
|
pValue >>= 7;
|
|
|
|
if (((pValue == 0) && ((byte & 0x40) == 0)) ||
|
|
((pValue == -1) && ((byte & 0x40) == 0x40)))
|
|
more = false;
|
|
else
|
|
byte |= 0x80;
|
|
|
|
*pBuf++ = byte;
|
|
size++;
|
|
} while (more);
|
|
|
|
return size;
|
|
}
|
|
|
|
template <>
|
|
size_t encode<int32_t>(ByteType*& pBuf, int32_t pValue) {
|
|
return encode<int64_t>(pBuf, static_cast<int64_t>(pValue));
|
|
}
|
|
|
|
//===---------------------- LEB128 Decoding APIs -------------------------===//
|
|
|
|
template <>
|
|
uint64_t decode<uint64_t>(const ByteType* pBuf, size_t& pSize) {
|
|
uint64_t result = 0;
|
|
|
|
if ((*pBuf & 0x80) == 0) {
|
|
pSize = 1;
|
|
return *pBuf;
|
|
} else if ((*(pBuf + 1) & 0x80) == 0) {
|
|
pSize = 2;
|
|
return ((*(pBuf + 1) & 0x7f) << 7) | (*pBuf & 0x7f);
|
|
} else if ((*(pBuf + 2) & 0x80) == 0) {
|
|
pSize = 3;
|
|
return ((*(pBuf + 2) & 0x7f) << 14) | ((*(pBuf + 1) & 0x7f) << 7) |
|
|
(*pBuf & 0x7f);
|
|
} else {
|
|
pSize = 4;
|
|
result = ((*(pBuf + 3) & 0x7f) << 21) | ((*(pBuf + 2) & 0x7f) << 14) |
|
|
((*(pBuf + 1) & 0x7f) << 7) | (*pBuf & 0x7f);
|
|
}
|
|
|
|
if ((*(pBuf + 3) & 0x80) != 0) {
|
|
// Large number which is an unusual case.
|
|
unsigned shift;
|
|
ByteType byte;
|
|
|
|
// Start the read from the 4th byte.
|
|
shift = 28;
|
|
pBuf += 4;
|
|
do {
|
|
byte = *pBuf;
|
|
pBuf++;
|
|
pSize++;
|
|
result |= (static_cast<uint64_t>(byte & 0x7f) << shift);
|
|
shift += 7;
|
|
} while (byte & 0x80);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <>
|
|
uint64_t decode<uint64_t>(const ByteType*& pBuf) {
|
|
ByteType byte;
|
|
uint64_t result;
|
|
|
|
byte = *pBuf++;
|
|
result = byte & 0x7f;
|
|
if ((byte & 0x80) == 0) {
|
|
return result;
|
|
} else {
|
|
byte = *pBuf++;
|
|
result |= ((byte & 0x7f) << 7);
|
|
if ((byte & 0x80) == 0) {
|
|
return result;
|
|
} else {
|
|
byte = *pBuf++;
|
|
result |= (byte & 0x7f) << 14;
|
|
if ((byte & 0x80) == 0) {
|
|
return result;
|
|
} else {
|
|
byte = *pBuf++;
|
|
result |= (byte & 0x7f) << 21;
|
|
if ((byte & 0x80) == 0) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Large number which is an unusual case.
|
|
unsigned shift;
|
|
|
|
// Start the read from the 4th byte.
|
|
shift = 28;
|
|
do {
|
|
byte = *pBuf++;
|
|
result |= (static_cast<uint64_t>(byte & 0x7f) << shift);
|
|
shift += 7;
|
|
} while (byte & 0x80);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Signed LEB128 decoding is Similar to the unsigned version but setup the sign
|
|
* bit if necessary. This is rarely used, therefore we don't provide unrolling
|
|
* version like decode() to save the code size.
|
|
*/
|
|
template <>
|
|
int64_t decode<int64_t>(const ByteType* pBuf, size_t& pSize) {
|
|
uint64_t result = 0;
|
|
ByteType byte;
|
|
unsigned shift = 0;
|
|
|
|
pSize = 0;
|
|
do {
|
|
byte = *pBuf;
|
|
pBuf++;
|
|
pSize++;
|
|
result |= (static_cast<uint64_t>(byte & 0x7f) << shift);
|
|
shift += 7;
|
|
} while (byte & 0x80);
|
|
|
|
if ((shift < (8 * sizeof(result))) && (byte & 0x40))
|
|
result |= ((static_cast<uint64_t>(-1)) << shift);
|
|
|
|
return result;
|
|
}
|
|
|
|
template <>
|
|
int64_t decode<int64_t>(const ByteType*& pBuf) {
|
|
uint64_t result = 0;
|
|
ByteType byte;
|
|
unsigned shift = 0;
|
|
|
|
do {
|
|
byte = *pBuf;
|
|
pBuf++;
|
|
result |= (static_cast<uint64_t>(byte & 0x7f) << shift);
|
|
shift += 7;
|
|
} while (byte & 0x80);
|
|
|
|
if ((shift < (8 * sizeof(result))) && (byte & 0x40))
|
|
result |= ((static_cast<uint64_t>(-1)) << shift);
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace leb128
|
|
} // namespace mcld
|