blob: 7528350cf759b280c057a38abc800ca2615aca4c [file] [log] [blame]
//===-- StringExtractor.cpp -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Utility/StringExtractor.h"
#include <tuple>
#include <ctype.h> // for isxdigit, isspace
#include <stdlib.h>
#include <string.h> // for memset
static inline int xdigit_to_sint(char ch) {
if (ch >= 'a' && ch <= 'f')
return 10 + ch - 'a';
if (ch >= 'A' && ch <= 'F')
return 10 + ch - 'A';
if (ch >= '0' && ch <= '9')
return ch - '0';
return -1;
}
//----------------------------------------------------------------------
// StringExtractor constructor
//----------------------------------------------------------------------
StringExtractor::StringExtractor() : m_packet(), m_index(0) {}
StringExtractor::StringExtractor(llvm::StringRef packet_str)
: m_packet(), m_index(0) {
m_packet.assign(packet_str.begin(), packet_str.end());
}
StringExtractor::StringExtractor(const char *packet_cstr)
: m_packet(), m_index(0) {
if (packet_cstr)
m_packet.assign(packet_cstr);
}
//----------------------------------------------------------------------
// StringExtractor copy constructor
//----------------------------------------------------------------------
StringExtractor::StringExtractor(const StringExtractor &rhs)
: m_packet(rhs.m_packet), m_index(rhs.m_index) {}
//----------------------------------------------------------------------
// StringExtractor assignment operator
//----------------------------------------------------------------------
const StringExtractor &StringExtractor::operator=(const StringExtractor &rhs) {
if (this != &rhs) {
m_packet = rhs.m_packet;
m_index = rhs.m_index;
}
return *this;
}
//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
StringExtractor::~StringExtractor() {}
char StringExtractor::GetChar(char fail_value) {
if (m_index < m_packet.size()) {
char ch = m_packet[m_index];
++m_index;
return ch;
}
m_index = UINT64_MAX;
return fail_value;
}
//----------------------------------------------------------------------
// If a pair of valid hex digits exist at the head of the StringExtractor they
// are decoded into an unsigned byte and returned by this function
//
// If there is not a pair of valid hex digits at the head of the
// StringExtractor, it is left unchanged and -1 is returned
//----------------------------------------------------------------------
int StringExtractor::DecodeHexU8() {
SkipSpaces();
if (GetBytesLeft() < 2) {
return -1;
}
const int hi_nibble = xdigit_to_sint(m_packet[m_index]);
const int lo_nibble = xdigit_to_sint(m_packet[m_index + 1]);
if (hi_nibble == -1 || lo_nibble == -1) {
return -1;
}
m_index += 2;
return (uint8_t)((hi_nibble << 4) + lo_nibble);
}
//----------------------------------------------------------------------
// Extract an unsigned character from two hex ASCII chars in the packet string,
// or return fail_value on failure
//----------------------------------------------------------------------
uint8_t StringExtractor::GetHexU8(uint8_t fail_value, bool set_eof_on_fail) {
// On success, fail_value will be overwritten with the next character in the
// stream
GetHexU8Ex(fail_value, set_eof_on_fail);
return fail_value;
}
bool StringExtractor::GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail) {
int byte = DecodeHexU8();
if (byte == -1) {
if (set_eof_on_fail || m_index >= m_packet.size())
m_index = UINT64_MAX;
// ch should not be changed in case of failure
return false;
}
ch = (uint8_t)byte;
return true;
}
uint32_t StringExtractor::GetU32(uint32_t fail_value, int base) {
if (m_index < m_packet.size()) {
char *end = nullptr;
const char *start = m_packet.c_str();
const char *cstr = start + m_index;
uint32_t result = static_cast<uint32_t>(::strtoul(cstr, &end, base));
if (end && end != cstr) {
m_index = end - start;
return result;
}
}
return fail_value;
}
int32_t StringExtractor::GetS32(int32_t fail_value, int base) {
if (m_index < m_packet.size()) {
char *end = nullptr;
const char *start = m_packet.c_str();
const char *cstr = start + m_index;
int32_t result = static_cast<int32_t>(::strtol(cstr, &end, base));
if (end && end != cstr) {
m_index = end - start;
return result;
}
}
return fail_value;
}
uint64_t StringExtractor::GetU64(uint64_t fail_value, int base) {
if (m_index < m_packet.size()) {
char *end = nullptr;
const char *start = m_packet.c_str();
const char *cstr = start + m_index;
uint64_t result = ::strtoull(cstr, &end, base);
if (end && end != cstr) {
m_index = end - start;
return result;
}
}
return fail_value;
}
int64_t StringExtractor::GetS64(int64_t fail_value, int base) {
if (m_index < m_packet.size()) {
char *end = nullptr;
const char *start = m_packet.c_str();
const char *cstr = start + m_index;
int64_t result = ::strtoll(cstr, &end, base);
if (end && end != cstr) {
m_index = end - start;
return result;
}
}
return fail_value;
}
uint32_t StringExtractor::GetHexMaxU32(bool little_endian,
uint32_t fail_value) {
uint32_t result = 0;
uint32_t nibble_count = 0;
SkipSpaces();
if (little_endian) {
uint32_t shift_amount = 0;
while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
// Make sure we don't exceed the size of a uint32_t...
if (nibble_count >= (sizeof(uint32_t) * 2)) {
m_index = UINT64_MAX;
return fail_value;
}
uint8_t nibble_lo;
uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]);
++m_index;
if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
nibble_lo = xdigit_to_sint(m_packet[m_index]);
++m_index;
result |= ((uint32_t)nibble_hi << (shift_amount + 4));
result |= ((uint32_t)nibble_lo << shift_amount);
nibble_count += 2;
shift_amount += 8;
} else {
result |= ((uint32_t)nibble_hi << shift_amount);
nibble_count += 1;
shift_amount += 4;
}
}
} else {
while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
// Make sure we don't exceed the size of a uint32_t...
if (nibble_count >= (sizeof(uint32_t) * 2)) {
m_index = UINT64_MAX;
return fail_value;
}
uint8_t nibble = xdigit_to_sint(m_packet[m_index]);
// Big Endian
result <<= 4;
result |= nibble;
++m_index;
++nibble_count;
}
}
return result;
}
uint64_t StringExtractor::GetHexMaxU64(bool little_endian,
uint64_t fail_value) {
uint64_t result = 0;
uint32_t nibble_count = 0;
SkipSpaces();
if (little_endian) {
uint32_t shift_amount = 0;
while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
// Make sure we don't exceed the size of a uint64_t...
if (nibble_count >= (sizeof(uint64_t) * 2)) {
m_index = UINT64_MAX;
return fail_value;
}
uint8_t nibble_lo;
uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]);
++m_index;
if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
nibble_lo = xdigit_to_sint(m_packet[m_index]);
++m_index;
result |= ((uint64_t)nibble_hi << (shift_amount + 4));
result |= ((uint64_t)nibble_lo << shift_amount);
nibble_count += 2;
shift_amount += 8;
} else {
result |= ((uint64_t)nibble_hi << shift_amount);
nibble_count += 1;
shift_amount += 4;
}
}
} else {
while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
// Make sure we don't exceed the size of a uint64_t...
if (nibble_count >= (sizeof(uint64_t) * 2)) {
m_index = UINT64_MAX;
return fail_value;
}
uint8_t nibble = xdigit_to_sint(m_packet[m_index]);
// Big Endian
result <<= 4;
result |= nibble;
++m_index;
++nibble_count;
}
}
return result;
}
bool StringExtractor::ConsumeFront(const llvm::StringRef &str) {
llvm::StringRef S = GetStringRef();
if (!S.startswith(str))
return false;
else
m_index += str.size();
return true;
}
size_t StringExtractor::GetHexBytes(llvm::MutableArrayRef<uint8_t> dest,
uint8_t fail_fill_value) {
size_t bytes_extracted = 0;
while (!dest.empty() && GetBytesLeft() > 0) {
dest[0] = GetHexU8(fail_fill_value);
if (!IsGood())
break;
++bytes_extracted;
dest = dest.drop_front();
}
if (!dest.empty())
::memset(dest.data(), fail_fill_value, dest.size());
return bytes_extracted;
}
//----------------------------------------------------------------------
// Decodes all valid hex encoded bytes at the head of the StringExtractor,
// limited by dst_len.
//
// Returns the number of bytes successfully decoded
//----------------------------------------------------------------------
size_t StringExtractor::GetHexBytesAvail(llvm::MutableArrayRef<uint8_t> dest) {
size_t bytes_extracted = 0;
while (!dest.empty()) {
int decode = DecodeHexU8();
if (decode == -1)
break;
dest[0] = (uint8_t)decode;
dest = dest.drop_front();
++bytes_extracted;
}
return bytes_extracted;
}
// Consume ASCII hex nibble character pairs until we have decoded byte_size
// bytes of data.
uint64_t StringExtractor::GetHexWithFixedSize(uint32_t byte_size,
bool little_endian,
uint64_t fail_value) {
if (byte_size <= 8 && GetBytesLeft() >= byte_size * 2) {
uint64_t result = 0;
uint32_t i;
if (little_endian) {
// Little Endian
uint32_t shift_amount;
for (i = 0, shift_amount = 0; i < byte_size && IsGood();
++i, shift_amount += 8) {
result |= ((uint64_t)GetHexU8() << shift_amount);
}
} else {
// Big Endian
for (i = 0; i < byte_size && IsGood(); ++i) {
result <<= 8;
result |= GetHexU8();
}
}
}
m_index = UINT64_MAX;
return fail_value;
}
size_t StringExtractor::GetHexByteString(std::string &str) {
str.clear();
str.reserve(GetBytesLeft() / 2);
char ch;
while ((ch = GetHexU8()) != '\0')
str.append(1, ch);
return str.size();
}
size_t StringExtractor::GetHexByteStringFixedLength(std::string &str,
uint32_t nibble_length) {
str.clear();
uint32_t nibble_count = 0;
for (const char *pch = Peek();
(nibble_count < nibble_length) && (pch != nullptr);
str.append(1, GetHexU8(0, false)), pch = Peek(), nibble_count += 2) {
}
return str.size();
}
size_t StringExtractor::GetHexByteStringTerminatedBy(std::string &str,
char terminator) {
str.clear();
char ch;
while ((ch = GetHexU8(0, false)) != '\0')
str.append(1, ch);
if (Peek() && *Peek() == terminator)
return str.size();
str.clear();
return str.size();
}
bool StringExtractor::GetNameColonValue(llvm::StringRef &name,
llvm::StringRef &value) {
// Read something in the form of NNNN:VVVV; where NNNN is any character that
// is not a colon, followed by a ':' character, then a value (one or more ';'
// chars), followed by a ';'
if (m_index >= m_packet.size())
return fail();
llvm::StringRef view(m_packet);
if (view.empty())
return fail();
llvm::StringRef a, b, c, d;
view = view.substr(m_index);
std::tie(a, b) = view.split(':');
if (a.empty() || b.empty())
return fail();
std::tie(c, d) = b.split(';');
if (b == c && d.empty())
return fail();
name = a;
value = c;
if (d.empty())
m_index = m_packet.size();
else {
size_t bytes_consumed = d.data() - view.data();
m_index += bytes_consumed;
}
return true;
}
void StringExtractor::SkipSpaces() {
const size_t n = m_packet.size();
while (m_index < n && isspace(m_packet[m_index]))
++m_index;
}