xref: /freebsd/contrib/llvm-project/lldb/source/Utility/StringExtractor.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1 //===-- StringExtractor.cpp -----------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Utility/StringExtractor.h"
10 #include "llvm/ADT/StringExtras.h"
11 
12 #include <tuple>
13 
14 #include <cctype>
15 #include <cstdlib>
16 #include <cstring>
17 
18 static inline int xdigit_to_sint(char ch) {
19   if (ch >= 'a' && ch <= 'f')
20     return 10 + ch - 'a';
21   if (ch >= 'A' && ch <= 'F')
22     return 10 + ch - 'A';
23   if (ch >= '0' && ch <= '9')
24     return ch - '0';
25   return -1;
26 }
27 
28 // StringExtractor constructor
29 StringExtractor::StringExtractor() : m_packet() {}
30 
31 StringExtractor::StringExtractor(llvm::StringRef packet_str) : m_packet() {
32   m_packet.assign(packet_str.begin(), packet_str.end());
33 }
34 
35 StringExtractor::StringExtractor(const char *packet_cstr) : m_packet() {
36   if (packet_cstr)
37     m_packet.assign(packet_cstr);
38 }
39 
40 // Destructor
41 StringExtractor::~StringExtractor() = default;
42 
43 char StringExtractor::GetChar(char fail_value) {
44   if (m_index < m_packet.size()) {
45     char ch = m_packet[m_index];
46     ++m_index;
47     return ch;
48   }
49   m_index = UINT64_MAX;
50   return fail_value;
51 }
52 
53 // If a pair of valid hex digits exist at the head of the StringExtractor they
54 // are decoded into an unsigned byte and returned by this function
55 //
56 // If there is not a pair of valid hex digits at the head of the
57 // StringExtractor, it is left unchanged and -1 is returned
58 int StringExtractor::DecodeHexU8() {
59   SkipSpaces();
60   if (GetBytesLeft() < 2) {
61     return -1;
62   }
63   const int hi_nibble = xdigit_to_sint(m_packet[m_index]);
64   const int lo_nibble = xdigit_to_sint(m_packet[m_index + 1]);
65   if (hi_nibble == -1 || lo_nibble == -1) {
66     return -1;
67   }
68   m_index += 2;
69   return static_cast<uint8_t>((hi_nibble << 4) + lo_nibble);
70 }
71 
72 // Extract an unsigned character from two hex ASCII chars in the packet string,
73 // or return fail_value on failure
74 uint8_t StringExtractor::GetHexU8(uint8_t fail_value, bool set_eof_on_fail) {
75   // On success, fail_value will be overwritten with the next character in the
76   // stream
77   GetHexU8Ex(fail_value, set_eof_on_fail);
78   return fail_value;
79 }
80 
81 bool StringExtractor::GetHexU8Ex(uint8_t &ch, bool set_eof_on_fail) {
82   int byte = DecodeHexU8();
83   if (byte == -1) {
84     if (set_eof_on_fail || m_index >= m_packet.size())
85       m_index = UINT64_MAX;
86     // ch should not be changed in case of failure
87     return false;
88   }
89   ch = static_cast<uint8_t>(byte);
90   return true;
91 }
92 
93 uint32_t StringExtractor::GetU32(uint32_t fail_value, int base) {
94   if (m_index < m_packet.size()) {
95     char *end = nullptr;
96     const char *start = m_packet.c_str();
97     const char *cstr = start + m_index;
98     uint32_t result = static_cast<uint32_t>(::strtoul(cstr, &end, base));
99 
100     if (end && end != cstr) {
101       m_index = end - start;
102       return result;
103     }
104   }
105   return fail_value;
106 }
107 
108 int32_t StringExtractor::GetS32(int32_t fail_value, int base) {
109   if (m_index < m_packet.size()) {
110     char *end = nullptr;
111     const char *start = m_packet.c_str();
112     const char *cstr = start + m_index;
113     int32_t result = static_cast<int32_t>(::strtol(cstr, &end, base));
114 
115     if (end && end != cstr) {
116       m_index = end - start;
117       return result;
118     }
119   }
120   return fail_value;
121 }
122 
123 uint64_t StringExtractor::GetU64(uint64_t fail_value, int base) {
124   if (m_index < m_packet.size()) {
125     char *end = nullptr;
126     const char *start = m_packet.c_str();
127     const char *cstr = start + m_index;
128     uint64_t result = ::strtoull(cstr, &end, base);
129 
130     if (end && end != cstr) {
131       m_index = end - start;
132       return result;
133     }
134   }
135   return fail_value;
136 }
137 
138 int64_t StringExtractor::GetS64(int64_t fail_value, int base) {
139   if (m_index < m_packet.size()) {
140     char *end = nullptr;
141     const char *start = m_packet.c_str();
142     const char *cstr = start + m_index;
143     int64_t result = ::strtoll(cstr, &end, base);
144 
145     if (end && end != cstr) {
146       m_index = end - start;
147       return result;
148     }
149   }
150   return fail_value;
151 }
152 
153 uint32_t StringExtractor::GetHexMaxU32(bool little_endian,
154                                        uint32_t fail_value) {
155   uint32_t result = 0;
156   uint32_t nibble_count = 0;
157 
158   SkipSpaces();
159   if (little_endian) {
160     uint32_t shift_amount = 0;
161     while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
162       // Make sure we don't exceed the size of a uint32_t...
163       if (nibble_count >= (sizeof(uint32_t) * 2)) {
164         m_index = UINT64_MAX;
165         return fail_value;
166       }
167 
168       uint8_t nibble_lo;
169       uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]);
170       ++m_index;
171       if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
172         nibble_lo = xdigit_to_sint(m_packet[m_index]);
173         ++m_index;
174         result |= (static_cast<uint32_t>(nibble_hi) << (shift_amount + 4));
175         result |= (static_cast<uint32_t>(nibble_lo) << shift_amount);
176         nibble_count += 2;
177         shift_amount += 8;
178       } else {
179         result |= (static_cast<uint32_t>(nibble_hi) << shift_amount);
180         nibble_count += 1;
181         shift_amount += 4;
182       }
183     }
184   } else {
185     while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
186       // Make sure we don't exceed the size of a uint32_t...
187       if (nibble_count >= (sizeof(uint32_t) * 2)) {
188         m_index = UINT64_MAX;
189         return fail_value;
190       }
191 
192       uint8_t nibble = xdigit_to_sint(m_packet[m_index]);
193       // Big Endian
194       result <<= 4;
195       result |= nibble;
196 
197       ++m_index;
198       ++nibble_count;
199     }
200   }
201   return result;
202 }
203 
204 uint64_t StringExtractor::GetHexMaxU64(bool little_endian,
205                                        uint64_t fail_value) {
206   uint64_t result = 0;
207   uint32_t nibble_count = 0;
208 
209   SkipSpaces();
210   if (little_endian) {
211     uint32_t shift_amount = 0;
212     while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
213       // Make sure we don't exceed the size of a uint64_t...
214       if (nibble_count >= (sizeof(uint64_t) * 2)) {
215         m_index = UINT64_MAX;
216         return fail_value;
217       }
218 
219       uint8_t nibble_lo;
220       uint8_t nibble_hi = xdigit_to_sint(m_packet[m_index]);
221       ++m_index;
222       if (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
223         nibble_lo = xdigit_to_sint(m_packet[m_index]);
224         ++m_index;
225         result |= (static_cast<uint64_t>(nibble_hi) << (shift_amount + 4));
226         result |= (static_cast<uint64_t>(nibble_lo) << shift_amount);
227         nibble_count += 2;
228         shift_amount += 8;
229       } else {
230         result |= (static_cast<uint64_t>(nibble_hi) << shift_amount);
231         nibble_count += 1;
232         shift_amount += 4;
233       }
234     }
235   } else {
236     while (m_index < m_packet.size() && ::isxdigit(m_packet[m_index])) {
237       // Make sure we don't exceed the size of a uint64_t...
238       if (nibble_count >= (sizeof(uint64_t) * 2)) {
239         m_index = UINT64_MAX;
240         return fail_value;
241       }
242 
243       uint8_t nibble = xdigit_to_sint(m_packet[m_index]);
244       // Big Endian
245       result <<= 4;
246       result |= nibble;
247 
248       ++m_index;
249       ++nibble_count;
250     }
251   }
252   return result;
253 }
254 
255 bool StringExtractor::ConsumeFront(const llvm::StringRef &str) {
256   llvm::StringRef S = GetStringRef();
257   if (!S.starts_with(str))
258     return false;
259   else
260     m_index += str.size();
261   return true;
262 }
263 
264 size_t StringExtractor::GetHexBytes(llvm::MutableArrayRef<uint8_t> dest,
265                                     uint8_t fail_fill_value) {
266   size_t bytes_extracted = 0;
267   while (!dest.empty() && GetBytesLeft() > 0) {
268     dest[0] = GetHexU8(fail_fill_value);
269     if (!IsGood())
270       break;
271     ++bytes_extracted;
272     dest = dest.drop_front();
273   }
274 
275   if (!dest.empty())
276     ::memset(dest.data(), fail_fill_value, dest.size());
277 
278   return bytes_extracted;
279 }
280 
281 // Decodes all valid hex encoded bytes at the head of the StringExtractor,
282 // limited by dst_len.
283 //
284 // Returns the number of bytes successfully decoded
285 size_t StringExtractor::GetHexBytesAvail(llvm::MutableArrayRef<uint8_t> dest) {
286   size_t bytes_extracted = 0;
287   while (!dest.empty()) {
288     int decode = DecodeHexU8();
289     if (decode == -1)
290       break;
291     dest[0] = static_cast<uint8_t>(decode);
292     dest = dest.drop_front();
293     ++bytes_extracted;
294   }
295   return bytes_extracted;
296 }
297 
298 size_t StringExtractor::GetHexByteString(std::string &str) {
299   str.clear();
300   str.reserve(GetBytesLeft() / 2);
301   char ch;
302   while ((ch = GetHexU8()) != '\0')
303     str.append(1, ch);
304   return str.size();
305 }
306 
307 size_t StringExtractor::GetHexByteStringFixedLength(std::string &str,
308                                                     uint32_t nibble_length) {
309   str.clear();
310 
311   uint32_t nibble_count = 0;
312   for (const char *pch = Peek();
313        (nibble_count < nibble_length) && (pch != nullptr);
314        str.append(1, GetHexU8(0, false)), pch = Peek(), nibble_count += 2) {
315   }
316 
317   return str.size();
318 }
319 
320 size_t StringExtractor::GetHexByteStringTerminatedBy(std::string &str,
321                                                      char terminator) {
322   str.clear();
323   char ch;
324   while ((ch = GetHexU8(0, false)) != '\0')
325     str.append(1, ch);
326   if (Peek() && *Peek() == terminator)
327     return str.size();
328 
329   str.clear();
330   return str.size();
331 }
332 
333 bool StringExtractor::GetNameColonValue(llvm::StringRef &name,
334                                         llvm::StringRef &value) {
335   // Read something in the form of NNNN:VVVV; where NNNN is any character that
336   // is not a colon, followed by a ':' character, then a value (one or more ';'
337   // chars), followed by a ';'
338   if (m_index >= m_packet.size())
339     return fail();
340 
341   llvm::StringRef view(m_packet);
342   if (view.empty())
343     return fail();
344 
345   llvm::StringRef a, b, c, d;
346   view = view.substr(m_index);
347   std::tie(a, b) = view.split(':');
348   if (a.empty() || b.empty())
349     return fail();
350   std::tie(c, d) = b.split(';');
351   if (b == c && d.empty())
352     return fail();
353 
354   name = a;
355   value = c;
356   if (d.empty())
357     m_index = m_packet.size();
358   else {
359     size_t bytes_consumed = d.data() - view.data();
360     m_index += bytes_consumed;
361   }
362   return true;
363 }
364 
365 void StringExtractor::SkipSpaces() {
366   const size_t n = m_packet.size();
367   while (m_index < n && llvm::isSpace(m_packet[m_index]))
368     ++m_index;
369 }
370