/ deps / KittyMemoryEx / KittyUtils.cpp
KittyUtils.cpp
  1  #include "KittyUtils.hpp"
  2  
  3  namespace KittyUtils
  4  {
  5  
  6      std::vector<uint8_t> randomBytes(std::size_t length)
  7      {
  8          static std::mutex mtx;
  9          std::lock_guard<std::mutex> lock(mtx);
 10  
 11          static std::mt19937 gen{std::random_device{}()};
 12  
 13          std::uniform_int_distribution<uint16_t> dist(0, 255);
 14  
 15          std::vector<uint8_t> data(length);
 16          for (auto &b : data)
 17          {
 18              b = static_cast<uint8_t>(dist(gen));
 19          }
 20  
 21          return data;
 22      }
 23  
 24      std::string randomString(size_t length)
 25      {
 26          static const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 27  
 28          static std::mutex mtx;
 29          std::lock_guard<std::mutex> lock(mtx);
 30  
 31          static std::default_random_engine rnd(std::random_device{}());
 32  
 33          std::uniform_int_distribution<std::string::size_type> dist(0, chars.size() - 1);
 34  
 35          std::string str(length, '\0');
 36          for (size_t i = 0; i < length; ++i)
 37              str[i] = chars[dist(rnd)];
 38  
 39          return str;
 40      }
 41  
 42  #ifdef __ANDROID__
 43      int Android::getVersion()
 44      {
 45          static int ver = 0;
 46          if (ver > 0)
 47              return ver;
 48  
 49          ver = getSystemProperty<int>("ro.build.version.release", 0);
 50          return ver;
 51      }
 52  
 53      int Android::getSDK()
 54      {
 55          static int sdk = 0;
 56          if (sdk > 0)
 57              return sdk;
 58  
 59          sdk = getSystemProperty<int>("ro.build.version.sdk", 0);
 60          return sdk;
 61      }
 62  
 63      bool Android::is64BitSupported()
 64      {
 65          static bool once = false;
 66          static bool is64 = false;
 67          if (!once)
 68          {
 69              char value[0xff]{};
 70              if (__system_property_get("ro.product.cpu.abilist", value) == 0)
 71                  __system_property_get("ro.product.cpu.abi", value);
 72  
 73              std::string abi = value;
 74              is64 = abi.find("64") != std::string::npos;
 75              once = true;
 76          }
 77          return is64;
 78      }
 79  
 80      std::string Android::getAppInternalDataDir(const std::string &packageName)
 81      {
 82          std::string dir = getAppInternalCacheDir(packageName);
 83          if (!dir.empty())
 84          {
 85              return Path::fileDirectory(dir);
 86          }
 87          return dir;
 88      }
 89  
 90      std::string Android::getAppInternalFilesDir(const std::string &packageName)
 91      {
 92          std::string dir = getAppInternalCacheDir(packageName);
 93          if (!dir.empty())
 94          {
 95              dir = Path::fileDirectory(dir);
 96              dir += "/files";
 97          }
 98          return dir;
 99      }
100  
101      std::string Android::getAppInternalCacheDir(const std::string &packageName)
102      {
103          std::string dir = "/data/data/";
104          dir += packageName;
105          dir += "/cache";
106          if (access(dir.c_str(), F_OK) == 0)
107              return dir;
108  
109          dir = "/data/user/";
110          dir += std::to_string(getUserId());
111          dir += "/";
112          dir += packageName;
113          dir += "/cache";
114          if (access(dir.c_str(), F_OK) == 0)
115              return dir;
116  
117          return std::string();
118      }
119  #endif
120  
121      std::string Path::fileName(const std::string &filePath)
122      {
123          std::string filename;
124          const size_t last_slash_idx = filePath.find_last_of("/\\");
125          if (std::string::npos != last_slash_idx)
126              filename = filePath.substr(last_slash_idx + 1);
127          return filename;
128      }
129  
130      std::string Path::fileDirectory(const std::string &filePath)
131      {
132          std::string directory;
133          const size_t last_slash_idx = filePath.find_last_of("/\\");
134          if (std::string::npos != last_slash_idx)
135              directory = filePath.substr(0, last_slash_idx);
136          return directory;
137      }
138  
139      std::string Path::fileExtension(const std::string &filePath)
140      {
141          std::string ext;
142          const size_t last_slash_idx = filePath.find_last_of(".");
143          if (std::string::npos != last_slash_idx)
144              ext = filePath.substr(last_slash_idx + 1);
145          return ext;
146      }
147  
148      bool String::startsWith(const std::string &str, const std::string &prefix, bool sensitive)
149      {
150          if (str.length() < prefix.length())
151              return false;
152          if (sensitive)
153          {
154              return str.compare(0, prefix.length(), prefix) == 0;
155          }
156          return std::equal(prefix.begin(), prefix.end(), str.begin(), charEqualsIgnoreCase);
157      }
158  
159      bool String::contains(const std::string &str, const std::string &substring, bool sensitive)
160      {
161          if (str.length() < substring.length())
162              return false;
163          if (sensitive)
164          {
165              return str.find(substring) != std::string::npos;
166          }
167          auto it = std::search(str.begin(), str.end(), substring.begin(), substring.end(), charEqualsIgnoreCase);
168          return it != str.end();
169      }
170  
171      bool String::endsWith(const std::string &str, const std::string &suffix, bool sensitive)
172      {
173          if (str.length() < suffix.length())
174              return false;
175          if (sensitive)
176          {
177              return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
178          }
179          return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin(), charEqualsIgnoreCase);
180      }
181  
182      bool String::startsWith(const std::string &str, const std::vector<std::string> &prefixes, bool sensitive)
183      {
184          for (const auto &prefix : prefixes)
185          {
186              if (startsWith(str, prefix, sensitive))
187                  return true;
188          }
189          return false;
190      }
191  
192      bool String::contains(const std::string &str, const std::vector<std::string> &substrings, bool sensitive)
193      {
194          for (const auto &substring : substrings)
195          {
196              if (contains(str, substring, sensitive))
197                  return true;
198          }
199          return false;
200      }
201  
202      bool String::endsWith(const std::string &str, const std::vector<std::string> &suffixes, bool sensitive)
203      {
204          for (const auto &suffix : suffixes)
205          {
206              if (endsWith(str, suffix, sensitive))
207                  return true;
208          }
209          return false;
210      }
211  
212      void String::trim(std::string &str)
213      {
214          str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end());
215      }
216  
217      bool String::isValidHex(const std::string &hex)
218      {
219          if (hex.empty())
220              return false;
221  
222          const char *data = hex.c_str();
223          size_t len = hex.length();
224          size_t i = 0;
225  
226          while (i < len && ::isspace(static_cast<unsigned char>(data[i])))
227          {
228              i++;
229          }
230  
231          if (i + 2 <= len && data[i] == '0' && (data[i + 1] == 'x' || data[i + 1] == 'X'))
232          {
233              i += 2;
234          }
235  
236          size_t digitCount = 0;
237  
238          for (; i < len; ++i)
239          {
240              unsigned char c = static_cast<unsigned char>(data[i]);
241  
242              if (::isspace(c))
243              {
244                  continue;
245              }
246  
247              if (!::isxdigit(c))
248              {
249                  return false;
250              }
251  
252              digitCount++;
253          }
254  
255          return (digitCount > 0 && (digitCount % 2 == 0));
256      }
257  
258      bool String::validateHex(std::string &hex)
259      {
260          if (hex.empty())
261              return false;
262  
263          size_t len = hex.length();
264          size_t startOffset = (len >= 2 && hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')) ? 2 : 0;
265  
266          size_t actualByteCount = 0;
267          bool needsCleaning = (startOffset > 0);
268  
269          for (size_t i = startOffset; i < len; ++i)
270          {
271              unsigned char c = static_cast<unsigned char>(hex[i]);
272  
273              if (::isspace(c))
274              {
275                  needsCleaning = true;
276                  continue;
277              }
278  
279              if (!::isxdigit(c))
280                  return false;
281  
282              actualByteCount++;
283          }
284  
285          if (actualByteCount == 0 || (actualByteCount % 2 != 0))
286              return false;
287  
288          if (needsCleaning)
289          {
290              std::string cleaned;
291              cleaned.reserve(actualByteCount);
292              for (size_t i = startOffset; i < len; ++i)
293              {
294                  unsigned char c = static_cast<unsigned char>(hex[i]);
295                  if (!::isspace(c))
296                  {
297                      cleaned.push_back(c);
298                  }
299              }
300              hex = std::move(cleaned);
301          }
302  
303          return true;
304      }
305  
306      std::string String::fmt(const char *fmt, ...)
307      {
308          if (!fmt)
309              return "";
310  
311          va_list args;
312          va_start(args, fmt);
313          int size = vsnprintf(nullptr, 0, fmt, args);
314          va_end(args);
315  
316          if (size <= 0)
317              return "";
318  
319          std::string str;
320          str.resize(static_cast<size_t>(size));
321  
322          va_start(args, fmt);
323          vsnprintf(&str[0], static_cast<size_t>(size) + 1, fmt, args);
324          va_end(args);
325  
326          return str;
327      }
328  
329      bool Data::fromHex(std::string in, void *data)
330      {
331          if (in.empty() || !data || !String::validateHex(in))
332              return false;
333  
334          size_t length = in.length();
335          auto *byteData = reinterpret_cast<uint8_t *>(data);
336  
337          auto charToNibble = [](char c) -> uint8_t {
338              if (c >= '0' && c <= '9')
339                  return c - '0';
340              if (c >= 'a' && c <= 'f')
341                  return c - 'a' + 10;
342              if (c >= 'A' && c <= 'F')
343                  return c - 'A' + 10;
344              return 0;
345          };
346  
347          for (size_t strIndex = 0, dataIndex = 0; strIndex < length; strIndex += 2, ++dataIndex)
348          {
349              byteData[dataIndex] = (charToNibble(in[strIndex]) << 4) | charToNibble(in[strIndex + 1]);
350          }
351  
352          return true;
353      }
354  
355      std::string Data::toHex(const void *data, const size_t dataLength)
356      {
357          if (!data || dataLength == 0)
358              return "";
359  
360          static const char hexTable[] = "0123456789ABCDEF";
361          const auto *byteData = reinterpret_cast<const uint8_t *>(data);
362  
363          std::string hexString;
364          hexString.resize(dataLength * 2);
365  
366          for (size_t i = 0; i < dataLength; ++i)
367          {
368              hexString[i * 2] = hexTable[(byteData[i] >> 4) & 0x0F];
369              hexString[i * 2 + 1] = hexTable[byteData[i] & 0x0F];
370          }
371  
372          return hexString;
373      }
374  
375      namespace Zip
376      {
377  #define KT_MIN_EOCD_SIZE 22
378  #define KT_EOCD_SIGNATURE 0x06054b50
379  #define KT_ZIP64_EOCD_SIGNATURE 0x06064b50
380  #define KT_ZIP64_EOCD_LOCATOR 0x07064b50
381  #define KT_CENTRAL_DIR_SIGNATURE 0x02014b50
382  #define KT_LOCAL_HEADER_SIGNATURE 0x04034b50
383  #define KT_ZIP64_EXTRA_ID 0x0001
384  #define KT_MAX_NAME_LEN 65535
385  #define KT_MAX_EOCD_SEARCH (1024 * 64)
386  #define KT_CENTRAL_DIR_SIZE 46
387  #define KT_LOCAL_HEADER_SIZE 30
388  
389          inline bool read16(const uint8_t *base, uint64_t size, uint64_t offset, uint16_t &out)
390          {
391              if (offset + 2 > size)
392                  return false;
393              std::memcpy(&out, base + offset, 2);
394              return true;
395          }
396  
397          inline bool read32(const uint8_t *base, uint64_t size, uint64_t offset, uint32_t &out)
398          {
399              if (offset + 4 > size)
400                  return false;
401              std::memcpy(&out, base + offset, 4);
402              return true;
403          }
404  
405          inline bool read64(const uint8_t *base, uint64_t size, uint64_t offset, uint64_t &out)
406          {
407              if (offset + 8 > size)
408                  return false;
409              std::memcpy(&out, base + offset, 8);
410              return true;
411          }
412  
413          bool findCentralDirectory(const uint8_t *data, uint64_t fileSize, uint64_t *cdOffset, uint64_t *totalEntries)
414          {
415              if (fileSize < KT_MIN_EOCD_SIZE)
416                  return false;
417  
418              uint64_t searchStart = (fileSize > KT_MAX_EOCD_SEARCH) ? fileSize - KT_MAX_EOCD_SEARCH : 0;
419  
420              for (int64_t offset = fileSize - 4; offset >= (int64_t)searchStart; --offset)
421              {
422                  uint32_t sig;
423                  if (!read32(data, fileSize, offset, sig))
424                      continue;
425  
426                  if (sig == KT_EOCD_SIGNATURE)
427                  {
428                      uint16_t entries16;
429                      uint32_t cdOff32;
430  
431                      if (!read16(data, fileSize, offset + 10, entries16))
432                          return false;
433                      if (!read32(data, fileSize, offset + 16, cdOff32))
434                          return false;
435  
436                      if (totalEntries)
437                          *totalEntries = entries16;
438  
439                      if (cdOffset)
440                          *cdOffset = cdOff32;
441  
442                      return true;
443                  }
444  
445                  if (sig == KT_ZIP64_EOCD_LOCATOR)
446                  {
447                      uint64_t zip64EOCDOffset;
448                      if (!read64(data, fileSize, offset + 8, zip64EOCDOffset))
449                          return false;
450  
451                      uint32_t zip64sig;
452                      if (!read32(data, fileSize, zip64EOCDOffset, zip64sig))
453                          return false;
454  
455                      if (zip64sig != KT_ZIP64_EOCD_SIGNATURE)
456                          return false;
457  
458                      uint64_t entries64;
459                      uint64_t cdOff64;
460  
461                      if (!read64(data, fileSize, zip64EOCDOffset + 24, entries64))
462                          return false;
463  
464                      if (!read64(data, fileSize, zip64EOCDOffset + 48, cdOff64))
465                          return false;
466  
467                      if (totalEntries)
468                          *totalEntries = entries64;
469  
470                      if (cdOffset)
471                          *cdOffset = cdOff64;
472  
473                      return true;
474                  }
475              }
476  
477              return false;
478          }
479  
480          std::vector<ZipEntryInfo> listEntriesInZip(const std::string &zipPath)
481          {
482              std::vector<ZipEntryInfo> ents;
483  
484              int fd = KT_EINTR_RETRY(open(zipPath.c_str(), O_RDONLY));
485              if (fd < 0)
486                  return ents;
487  
488              struct stat st{};
489              if (fstat(fd, &st) < 0)
490              {
491                  KT_EINTR_RETRY(close(fd));
492                  return ents;
493              }
494  
495              uint64_t fileSize = st.st_size;
496              if (fileSize < KT_MIN_EOCD_SIZE)
497              {
498                  KT_EINTR_RETRY(close(fd));
499                  return ents;
500              }
501  
502              void *map = mmap(nullptr, fileSize, PROT_READ, MAP_PRIVATE, fd, 0);
503              if (!map || map == MAP_FAILED)
504              {
505                  KT_EINTR_RETRY(close(fd));
506                  return ents;
507              }
508  
509              const uint8_t *data = static_cast<uint8_t *>(map);
510  
511              uint64_t cdOffset = 0;
512              uint64_t totalEntries = 0;
513  
514              if (!findCentralDirectory(data, fileSize, &cdOffset, &totalEntries))
515              {
516                  munmap(map, fileSize);
517                  KT_EINTR_RETRY(close(fd));
518                  return ents;
519              }
520  
521              if (cdOffset >= fileSize)
522              {
523                  munmap(map, fileSize);
524                  KT_EINTR_RETRY(close(fd));
525                  return ents;
526              }
527  
528              uint64_t offset = cdOffset;
529              uint64_t parsedEntries = 0;
530  
531              while (offset + KT_CENTRAL_DIR_SIZE <= fileSize)
532              {
533                  uint32_t sig;
534                  if (!read32(data, fileSize, offset, sig))
535                      break;
536  
537                  if (sig != KT_CENTRAL_DIR_SIGNATURE)
538                      break;
539  
540                  ZipEntryInfo info{};
541  
542                  read16(data, fileSize, offset + 10, info.compressionMethod);
543                  read16(data, fileSize, offset + 12, info.modTime);
544                  read16(data, fileSize, offset + 14, info.modDate);
545                  read32(data, fileSize, offset + 16, info.crc32);
546  
547                  uint32_t compSize32, uncompSize32;
548                  read32(data, fileSize, offset + 20, compSize32);
549                  read32(data, fileSize, offset + 24, uncompSize32);
550  
551                  info.compressedSize = compSize32;
552                  info.uncompressedSize = uncompSize32;
553  
554                  uint16_t nameLen, extraLen, commentLen;
555                  read16(data, fileSize, offset + 28, nameLen);
556                  read16(data, fileSize, offset + 30, extraLen);
557                  read16(data, fileSize, offset + 32, commentLen);
558  
559                  uint32_t localHeaderOffset32;
560                  read32(data, fileSize, offset + 42, localHeaderOffset32);
561  
562                  uint64_t entrySize = KT_CENTRAL_DIR_SIZE + nameLen + extraLen + commentLen;
563                  if (offset + entrySize > fileSize)
564                      break;
565  
566                  if (nameLen > KT_MAX_NAME_LEN)
567                      break;
568  
569                  info.fileName.assign(reinterpret_cast<const char *>(data + offset + KT_CENTRAL_DIR_SIZE), nameLen);
570  
571                  uint64_t localHeaderOffset = localHeaderOffset32;
572  
573                  // ZIP64 handling
574                  if (compSize32 == 0xFFFFFFFF || uncompSize32 == 0xFFFFFFFF || localHeaderOffset32 == 0xFFFFFFFF)
575                  {
576                      uint64_t extraOffset = offset + KT_CENTRAL_DIR_SIZE + nameLen;
577                      uint64_t endExtra = extraOffset + extraLen;
578  
579                      while (extraOffset + 4 <= endExtra)
580                      {
581                          uint16_t id, size;
582                          read16(data, fileSize, extraOffset, id);
583                          read16(data, fileSize, extraOffset + 2, size);
584  
585                          if (extraOffset + 4 + size > endExtra)
586                              break;
587  
588                          if (id == KT_ZIP64_EXTRA_ID)
589                          {
590                              uint64_t fieldOffset = extraOffset + 4;
591  
592                              if (uncompSize32 == 0xFFFFFFFF)
593                              {
594                                  read64(data, fileSize, fieldOffset, info.uncompressedSize);
595                                  fieldOffset += 8;
596                              }
597  
598                              if (compSize32 == 0xFFFFFFFF)
599                              {
600                                  read64(data, fileSize, fieldOffset, info.compressedSize);
601                                  fieldOffset += 8;
602                              }
603  
604                              if (localHeaderOffset32 == 0xFFFFFFFF)
605                              {
606                                  read64(data, fileSize, fieldOffset, localHeaderOffset);
607                              }
608  
609                              break;
610                          }
611  
612                          extraOffset += 4 + size;
613                      }
614                  }
615  
616                  // Validate local header
617                  if (localHeaderOffset + KT_LOCAL_HEADER_SIZE > fileSize)
618                      break;
619  
620                  uint16_t localNameLen, localExtraLen;
621                  read16(data, fileSize, localHeaderOffset + 26, localNameLen);
622                  read16(data, fileSize, localHeaderOffset + 28, localExtraLen);
623  
624                  info.dataOffset = localHeaderOffset + KT_LOCAL_HEADER_SIZE + localNameLen + localExtraLen;
625  
626                  if (info.dataOffset > fileSize)
627                      break;
628  
629                  ents.push_back(std::move(info));
630  
631                  offset += entrySize;
632                  parsedEntries++;
633  
634                  if (parsedEntries >= totalEntries)
635                      break;
636              }
637  
638              munmap(map, fileSize);
639              KT_EINTR_RETRY(close(fd));
640  
641              return ents;
642          }
643  
644          bool findEntryInfoByDataOffset(const std::string &zipPath, uint64_t dataOffset, ZipEntryInfo *out)
645          {
646              if (out)
647                  *out = {};
648  
649              const auto ents = listEntriesInZip(zipPath);
650              for (const auto &it : ents)
651              {
652                  if (it.dataOffset == dataOffset)
653                  {
654                      if (out)
655                          *out = it;
656  
657                      return true;
658                  }
659              }
660  
661              return false;
662          }
663  
664          bool mmapEntryByDataOffset(const std::string &zipPath, uint64_t dataOffset, ZipEntryMMap *out)
665          {
666              if (out)
667                  *out = {};
668  
669              ZipEntryInfo ent{};
670              if (!findEntryInfoByDataOffset(zipPath, dataOffset, &ent))
671                  return false;
672  
673              uint64_t compressedSize = ent.compressedSize;
674  
675              int fd = KT_EINTR_RETRY(open(zipPath.c_str(), O_RDONLY));
676              if (fd < 0)
677                  return false;
678  
679              struct stat st{};
680              if (fstat(fd, &st) < 0)
681              {
682                  KT_EINTR_RETRY(close(fd));
683                  return false;
684              }
685  
686              uint64_t fileSize = st.st_size;
687  
688              if (dataOffset >= fileSize || dataOffset + compressedSize > fileSize)
689              {
690                  KT_EINTR_RETRY(close(fd));
691                  return false;
692              }
693  
694              const size_t pageSize = sysconf(_SC_PAGE_SIZE);
695              uint64_t alignedOffset = dataOffset & ~(uint64_t(pageSize - 1));
696              uint64_t offsetDiff = dataOffset - alignedOffset;
697              uint64_t mapSize = offsetDiff + compressedSize;
698  
699              void *map = mmap(nullptr, mapSize, PROT_READ, MAP_PRIVATE, fd, alignedOffset);
700  
701              KT_EINTR_RETRY(close(fd));
702  
703              if (!map || map == MAP_FAILED)
704                  return false;
705  
706              if (out)
707              {
708                  out->mappingBase = map;
709                  out->mappingSize = mapSize;
710                  out->data = static_cast<uint8_t *>(map) + offsetDiff;
711                  out->size = compressedSize;
712              }
713  
714              return true;
715          }
716      } // namespace Zip
717  
718  } // namespace KittyUtils