//===- 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 of leb128
} // namespace of mcld