428 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===-- 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;
 | |
| }
 |