mirror of
https://github.com/alterware/aw-installer.git
synced 2025-09-03 14:57:26 +00:00
init
This commit is contained in:
280
src/utils/compression.cpp
Normal file
280
src/utils/compression.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "compression.hpp"
|
||||
|
||||
#include <unzip.h>
|
||||
|
||||
#include <zlib.h>
|
||||
#include <zip.h>
|
||||
|
||||
#include <gsl/gsl>
|
||||
|
||||
#include "io.hpp"
|
||||
#include "string.hpp"
|
||||
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH 256
|
||||
#endif
|
||||
|
||||
namespace utils::compression
|
||||
{
|
||||
namespace zlib
|
||||
{
|
||||
namespace
|
||||
{
|
||||
class zlib_stream
|
||||
{
|
||||
public:
|
||||
zlib_stream()
|
||||
{
|
||||
memset(&stream_, 0, sizeof(stream_));
|
||||
valid_ = inflateInit(&stream_) == Z_OK;
|
||||
}
|
||||
|
||||
zlib_stream(zlib_stream&&) = delete;
|
||||
zlib_stream(const zlib_stream&) = delete;
|
||||
zlib_stream& operator=(zlib_stream&&) = delete;
|
||||
zlib_stream& operator=(const zlib_stream&) = delete;
|
||||
|
||||
~zlib_stream()
|
||||
{
|
||||
if (valid_)
|
||||
{
|
||||
inflateEnd(&stream_);
|
||||
}
|
||||
}
|
||||
|
||||
z_stream& get()
|
||||
{
|
||||
return stream_; //
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return valid_;
|
||||
}
|
||||
|
||||
private:
|
||||
bool valid_{false};
|
||||
z_stream stream_{};
|
||||
};
|
||||
}
|
||||
|
||||
std::string decompress(const std::string& data)
|
||||
{
|
||||
std::string buffer{};
|
||||
zlib_stream stream_container{};
|
||||
if (!stream_container.is_valid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
int ret{};
|
||||
size_t offset = 0;
|
||||
static thread_local uint8_t dest[CHUNK] = {0};
|
||||
auto& stream = stream_container.get();
|
||||
|
||||
do
|
||||
{
|
||||
const auto input_size = std::min(sizeof(dest), data.size() - offset);
|
||||
stream.avail_in = static_cast<uInt>(input_size);
|
||||
stream.next_in = reinterpret_cast<const Bytef*>(data.data()) + offset;
|
||||
offset += stream.avail_in;
|
||||
|
||||
do
|
||||
{
|
||||
stream.avail_out = sizeof(dest);
|
||||
stream.next_out = dest;
|
||||
|
||||
ret = inflate(&stream, Z_NO_FLUSH);
|
||||
if (ret != Z_OK && ret != Z_STREAM_END)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
buffer.insert(buffer.end(), dest, dest + sizeof(dest) - stream.avail_out);
|
||||
}
|
||||
while (stream.avail_out == 0);
|
||||
}
|
||||
while (ret != Z_STREAM_END);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string compress(const std::string& data)
|
||||
{
|
||||
std::string result{};
|
||||
auto length = compressBound(static_cast<uLong>(data.size()));
|
||||
result.resize(length);
|
||||
|
||||
if (compress2(reinterpret_cast<Bytef*>(result.data()), &length,
|
||||
reinterpret_cast<const Bytef*>(data.data()), static_cast<uLong>(data.size()),
|
||||
Z_BEST_COMPRESSION) != Z_OK)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
result.resize(length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
namespace zip
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool add_file(zipFile& zip_file, const std::string& filename, const std::string& data)
|
||||
{
|
||||
const auto zip_64 = data.size() > 0xffffffff ? 1 : 0;
|
||||
if (ZIP_OK != zipOpenNewFileInZip64(zip_file, filename.c_str(), nullptr, nullptr, 0, nullptr, 0, nullptr,
|
||||
Z_DEFLATED, Z_BEST_COMPRESSION, zip_64))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto _ = gsl::finally([&zip_file]() -> void
|
||||
{
|
||||
zipCloseFileInZip(zip_file);
|
||||
});
|
||||
|
||||
return ZIP_OK == zipWriteInFileInZip(zip_file, data.c_str(), static_cast<unsigned>(data.size()));
|
||||
}
|
||||
}
|
||||
|
||||
void archive::add(const std::string& filename, const std::string& data)
|
||||
{
|
||||
this->files_[filename] = data;
|
||||
}
|
||||
|
||||
bool archive::write(const std::string& filename, const std::string& comment)
|
||||
{
|
||||
// Hack to create the directory :3
|
||||
io::write_file(filename, {});
|
||||
io::remove_file(filename);
|
||||
|
||||
auto* zip_file = zipOpen64(filename.c_str(), false);
|
||||
if (!zip_file)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto _ = gsl::finally([&zip_file, &comment]() -> void
|
||||
{
|
||||
zipClose(zip_file, comment.empty() ? nullptr : comment.c_str());
|
||||
});
|
||||
|
||||
for (const auto& file : this->files_)
|
||||
{
|
||||
if (!add_file(zip_file, file.first, file.second))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// I apologize for writing such a huge function
|
||||
void archive::decompress(const std::string& filename, const std::filesystem::path& out_dir)
|
||||
{
|
||||
unzFile file = unzOpen(filename.c_str());
|
||||
if (!file)
|
||||
{
|
||||
throw std::runtime_error(string::va("unzOpen failed on %s", filename.c_str()));
|
||||
}
|
||||
|
||||
unz_global_info global_info;
|
||||
if (unzGetGlobalInfo(file, &global_info) != UNZ_OK)
|
||||
{
|
||||
unzClose(file);
|
||||
throw std::runtime_error(string::va("unzGetGlobalInfo failed on %s", filename.c_str()));
|
||||
}
|
||||
|
||||
constexpr std::size_t READ_BUFFER_SIZE = 65336;
|
||||
const auto read_buffer_large = std::make_unique<char[]>(READ_BUFFER_SIZE);
|
||||
// No need to memset this to 0
|
||||
auto* read_buffer = read_buffer_large.get();
|
||||
|
||||
// Loop to extract all the files
|
||||
for (uLong i = 0; i < global_info.number_entry; ++i)
|
||||
{
|
||||
// Get info about the current file.
|
||||
unz_file_info file_info;
|
||||
char filename_buffer[MAX_PATH]{};
|
||||
|
||||
if (unzGetCurrentFileInfo(file, &file_info, filename_buffer, sizeof(filename_buffer) - 1,
|
||||
nullptr, 0, nullptr, 0) != UNZ_OK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this entry is a directory or a file.
|
||||
std::string out_file = filename_buffer;
|
||||
// Fix for UNIX Systems
|
||||
std::replace(out_file.begin(), out_file.end(), '\\', '/');
|
||||
|
||||
const auto filename_length = out_file.size();
|
||||
if (out_file[filename_length - 1] == '/') // ZIP is not directory-separator-agnostic
|
||||
{
|
||||
// Entry is a directory. Create it.
|
||||
const auto dir = out_dir / out_file;
|
||||
io::create_directory(dir);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Entry is a file. Extract it.
|
||||
if (unzOpenCurrentFile(file) != UNZ_OK)
|
||||
{
|
||||
// Could not read file from the ZIP
|
||||
throw std::runtime_error(string::va("Failed to read file \"%s\" from \"%s\"", out_file.c_str(), filename.c_str()));
|
||||
}
|
||||
|
||||
const auto path = out_dir / out_file;
|
||||
// Must create any directories before opening a stream
|
||||
io::create_directory(path.parent_path());
|
||||
|
||||
// Open a stream to write out the data.
|
||||
std::ofstream out(path.string(), std::ios::binary | std::ios::trunc);
|
||||
if (!out.is_open())
|
||||
{
|
||||
throw std::runtime_error("Failed to open stream");
|
||||
}
|
||||
|
||||
auto read_bytes = UNZ_OK;
|
||||
while (true)
|
||||
{
|
||||
read_bytes = unzReadCurrentFile(file, read_buffer, READ_BUFFER_SIZE);
|
||||
if (read_bytes < 0)
|
||||
{
|
||||
throw std::runtime_error(string::va("Error while reading \"%s\" from the archive", out_file.c_str()));
|
||||
}
|
||||
|
||||
if (read_bytes > 0)
|
||||
{
|
||||
out.write(read_buffer, read_bytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more data to read, the loop will break
|
||||
// This is normal behaviour
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out.close();
|
||||
}
|
||||
|
||||
// Go the the next entry listed in the ZIP file.
|
||||
if ((i + 1) < global_info.number_entry)
|
||||
{
|
||||
if (unzGoToNextFile(file) != UNZ_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unzClose(file);
|
||||
}
|
||||
}
|
||||
}
|
31
src/utils/compression.hpp
Normal file
31
src/utils/compression.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#define CHUNK 16384u
|
||||
|
||||
namespace utils::compression
|
||||
{
|
||||
namespace zlib
|
||||
{
|
||||
std::string compress(const std::string& data);
|
||||
std::string decompress(const std::string& data);
|
||||
}
|
||||
|
||||
namespace zip
|
||||
{
|
||||
class archive
|
||||
{
|
||||
public:
|
||||
void add(const std::string& filename, const std::string& data);
|
||||
[[nodiscard]] bool write(const std::string& filename, const std::string& comment = {});
|
||||
|
||||
static void decompress(const std::string& filename, const std::filesystem::path& out_dir);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> files_;
|
||||
};
|
||||
}
|
||||
};
|
65
src/utils/http.cpp
Normal file
65
src/utils/http.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "http.hpp"
|
||||
#include <curl/curl.h>
|
||||
|
||||
namespace utils::http
|
||||
{
|
||||
namespace
|
||||
{
|
||||
size_t write_callback(void* contents, const size_t size, const size_t nmemb, void* userp)
|
||||
{
|
||||
static_cast<std::string*>(userp)->append(static_cast<char*>(contents), size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> get_data(const std::string& url, const headers& headers)
|
||||
{
|
||||
curl_slist* header_list = nullptr;
|
||||
auto* curl = curl_easy_init();
|
||||
if (!curl)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto _ = gsl::finally([&]()
|
||||
{
|
||||
curl_slist_free_all(header_list);
|
||||
curl_easy_cleanup(curl);
|
||||
});
|
||||
|
||||
|
||||
for(const auto& header : headers)
|
||||
{
|
||||
auto data = header.first + ": "s + header.second;
|
||||
header_list = curl_slist_append(header_list, data.c_str());
|
||||
}
|
||||
|
||||
std::string buffer{};
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "aw-installer/1.0");
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
|
||||
if (curl_easy_perform(curl) == CURLE_OK)
|
||||
{
|
||||
return {std::move(buffer)};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers)
|
||||
{
|
||||
return std::async(std::launch::async, [url, headers]() -> std::optional<std::string>
|
||||
{
|
||||
return get_data(url, headers);
|
||||
});
|
||||
}
|
||||
}
|
14
src/utils/http.hpp
Normal file
14
src/utils/http.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace utils::http
|
||||
{
|
||||
using headers = std::unordered_map<std::string, std::string>;
|
||||
|
||||
std::optional<std::string> get_data(const std::string& url, const headers& headers = {});
|
||||
std::future<std::optional<std::string>> get_data_async(const std::string& url, const headers& headers = {});
|
||||
}
|
136
src/utils/io.cpp
Normal file
136
src/utils/io.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "io.hpp"
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
|
||||
namespace utils::io
|
||||
{
|
||||
bool remove_file(const std::string& file)
|
||||
{
|
||||
return remove(file.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool move_file(const std::string& src, const std::string& target)
|
||||
{
|
||||
return rename(src.c_str(), target.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool file_exists(const std::string& file)
|
||||
{
|
||||
return std::ifstream(file).good();
|
||||
}
|
||||
|
||||
bool write_file(const std::string& file, const std::string& data, const bool append)
|
||||
{
|
||||
const auto pos = file.find_last_of("/\\");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
create_directory(file.substr(0, pos));
|
||||
}
|
||||
|
||||
auto mode = std::ios::binary | std::ofstream::out;
|
||||
if (append)
|
||||
{
|
||||
mode |= std::ofstream::app;
|
||||
}
|
||||
|
||||
std::ofstream stream(file, mode);
|
||||
|
||||
if (stream.is_open())
|
||||
{
|
||||
stream.write(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string read_file(const std::string& file)
|
||||
{
|
||||
std::string data;
|
||||
read_file(file, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool read_file(const std::string& file, std::string* data)
|
||||
{
|
||||
if (!data) return false;
|
||||
data->clear();
|
||||
|
||||
if (file_exists(file))
|
||||
{
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
if (!stream.is_open()) return false;
|
||||
|
||||
stream.seekg(0, std::ios::end);
|
||||
const std::streamsize size = stream.tellg();
|
||||
stream.seekg(0, std::ios::beg);
|
||||
|
||||
if (size > -1)
|
||||
{
|
||||
data->resize(static_cast<uint32_t>(size));
|
||||
stream.read(const_cast<char*>(data->data()), size);
|
||||
stream.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t file_size(const std::string& file)
|
||||
{
|
||||
if (file_exists(file))
|
||||
{
|
||||
std::ifstream stream(file, std::ios::binary);
|
||||
|
||||
if (stream.good())
|
||||
{
|
||||
stream.seekg(0, std::ios::end);
|
||||
return static_cast<std::size_t>(stream.tellg());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool create_directory(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec;
|
||||
return std::filesystem::create_directories(directory, ec);
|
||||
}
|
||||
|
||||
bool directory_exists(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec;
|
||||
return std::filesystem::is_directory(directory, ec);
|
||||
}
|
||||
|
||||
bool directory_is_empty(const std::filesystem::path& directory)
|
||||
{
|
||||
std::error_code ec;
|
||||
return std::filesystem::is_empty(directory, ec);
|
||||
}
|
||||
|
||||
std::vector<std::string> list_files(const std::filesystem::path& directory)
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
|
||||
for (auto& file : std::filesystem::directory_iterator(directory))
|
||||
{
|
||||
files.push_back(file.path().generic_string());
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target)
|
||||
{
|
||||
std::error_code ec;
|
||||
std::filesystem::copy(src, target,
|
||||
std::filesystem::copy_options::overwrite_existing |
|
||||
std::filesystem::copy_options::recursive, ec);
|
||||
}
|
||||
}
|
21
src/utils/io.hpp
Normal file
21
src/utils/io.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace utils::io
|
||||
{
|
||||
bool remove_file(const std::string& file);
|
||||
bool move_file(const std::string& src, const std::string& target);
|
||||
bool file_exists(const std::string& file);
|
||||
bool write_file(const std::string& file, const std::string& data, bool append = false);
|
||||
bool read_file(const std::string& file, std::string* data);
|
||||
std::string read_file(const std::string& file);
|
||||
std::size_t file_size(const std::string& file);
|
||||
bool create_directory(const std::filesystem::path& directory);
|
||||
bool directory_exists(const std::filesystem::path& directory);
|
||||
bool directory_is_empty(const std::filesystem::path& directory);
|
||||
std::vector<std::string> list_files(const std::filesystem::path& directory);
|
||||
void copy_folder(const std::filesystem::path& src, const std::filesystem::path& target);
|
||||
}
|
109
src/utils/memory.cpp
Normal file
109
src/utils/memory.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "memory.hpp"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
memory::allocator memory::mem_allocator_;
|
||||
|
||||
memory::allocator::~allocator()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void memory::allocator::clear()
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
for (auto& data : this->pool_)
|
||||
{
|
||||
memory::free(data);
|
||||
}
|
||||
|
||||
this->pool_.clear();
|
||||
}
|
||||
|
||||
void memory::allocator::free(void* data)
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
|
||||
if (j != this->pool_.end())
|
||||
{
|
||||
memory::free(data);
|
||||
this->pool_.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
void memory::allocator::free(const void* data)
|
||||
{
|
||||
this->free(const_cast<void*>(data));
|
||||
}
|
||||
|
||||
void* memory::allocator::allocate(const std::size_t length)
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto data = memory::allocate(length);
|
||||
this->pool_.push_back(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool memory::allocator::empty() const
|
||||
{
|
||||
return this->pool_.empty();
|
||||
}
|
||||
|
||||
char* memory::allocator::duplicate_string(const std::string& string)
|
||||
{
|
||||
std::lock_guard _(this->mutex_);
|
||||
|
||||
const auto data = memory::duplicate_string(string);
|
||||
this->pool_.push_back(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void* memory::allocate(const std::size_t length)
|
||||
{
|
||||
auto* buf = std::malloc(length);
|
||||
std::memset(buf, 0, length);
|
||||
return buf;
|
||||
}
|
||||
|
||||
char* memory::duplicate_string(const std::string& string)
|
||||
{
|
||||
const auto new_string = allocate_array<char>(string.size() + 1);
|
||||
std::memcpy(new_string, string.c_str(), string.size());
|
||||
return new_string;
|
||||
}
|
||||
|
||||
void memory::free(void* data)
|
||||
{
|
||||
::free(data);
|
||||
}
|
||||
|
||||
void memory::free(const void* data)
|
||||
{
|
||||
free(const_cast<void*>(data));
|
||||
}
|
||||
|
||||
bool memory::is_set(const void* mem, const char chr, const std::size_t length)
|
||||
{
|
||||
auto* const mem_arr = static_cast<const char*>(mem);
|
||||
|
||||
for (std::size_t i = 0; i < length; ++i)
|
||||
{
|
||||
if (mem_arr[i] != chr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
memory::allocator* memory::get_allocator()
|
||||
{
|
||||
return &memory::mem_allocator_;
|
||||
}
|
||||
}
|
71
src/utils/memory.hpp
Normal file
71
src/utils/memory.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class memory final
|
||||
{
|
||||
public:
|
||||
class allocator final
|
||||
{
|
||||
public:
|
||||
~allocator();
|
||||
|
||||
void clear();
|
||||
|
||||
void free(void* data);
|
||||
|
||||
void free(const void* data);
|
||||
|
||||
void* allocate(std::size_t length);
|
||||
|
||||
template <typename T>
|
||||
T* allocate()
|
||||
{
|
||||
return this->allocate_array<T>(1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* allocate_array(const std::size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
bool empty() const;
|
||||
|
||||
char* duplicate_string(const std::string& string);
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::vector<void*> pool_;
|
||||
};
|
||||
|
||||
static void* allocate(std::size_t length);
|
||||
|
||||
template <typename T>
|
||||
static T* allocate()
|
||||
{
|
||||
return allocate_array<T>(1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T* allocate_array(const std::size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(allocate(count * sizeof(T)));
|
||||
}
|
||||
|
||||
static char* duplicate_string(const std::string& string);
|
||||
|
||||
static void free(void* data);
|
||||
static void free(const void* data);
|
||||
|
||||
static bool is_set(const void* mem, char chr, std::size_t length);
|
||||
|
||||
static allocator* get_allocator();
|
||||
|
||||
private:
|
||||
static allocator mem_allocator_;
|
||||
};
|
||||
}
|
94
src/utils/properties.cpp
Normal file
94
src/utils/properties.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "io.hpp"
|
||||
#include "properties.hpp"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/prettywriter.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
|
||||
|
||||
namespace utils::properties
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::string get_properties_file()
|
||||
{
|
||||
return "properties.json";
|
||||
}
|
||||
|
||||
rapidjson::Document load_properties()
|
||||
{
|
||||
rapidjson::Document default_doc{};
|
||||
default_doc.SetObject();
|
||||
|
||||
std::string data{};
|
||||
const auto& props = get_properties_file();
|
||||
if (!io::read_file(props, &data))
|
||||
{
|
||||
return default_doc;
|
||||
}
|
||||
|
||||
rapidjson::Document doc{};
|
||||
const rapidjson::ParseResult result = doc.Parse(data);
|
||||
|
||||
if (!result || !doc.IsObject())
|
||||
{
|
||||
return default_doc;
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
void store_properties(const rapidjson::Document& doc)
|
||||
{
|
||||
rapidjson::StringBuffer buffer{};
|
||||
rapidjson::Writer<rapidjson::StringBuffer, rapidjson::Document::EncodingType, rapidjson::ASCII<>>
|
||||
writer(buffer);
|
||||
doc.Accept(writer);
|
||||
|
||||
const std::string json{ buffer.GetString(), buffer.GetLength() };
|
||||
|
||||
const auto& props = get_properties_file();
|
||||
io::write_file(props, json);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> load(const std::string& name)
|
||||
{
|
||||
const auto doc = load_properties();
|
||||
|
||||
if (!doc.HasMember(name))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto& value = doc[name];
|
||||
if (!value.IsString())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return { std::string{ value.GetString() } };
|
||||
}
|
||||
|
||||
void store(const std::string& name, const std::string& value)
|
||||
{
|
||||
auto doc = load_properties();
|
||||
|
||||
while (doc.HasMember(name))
|
||||
{
|
||||
doc.RemoveMember(name);
|
||||
}
|
||||
|
||||
rapidjson::Value key{};
|
||||
key.SetString(name, doc.GetAllocator());
|
||||
|
||||
rapidjson::Value member{};
|
||||
member.SetString(value, doc.GetAllocator());
|
||||
|
||||
doc.AddMember(key, member, doc.GetAllocator());
|
||||
|
||||
store_properties(doc);
|
||||
}
|
||||
}
|
10
src/utils/properties.hpp
Normal file
10
src/utils/properties.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
|
||||
namespace utils::properties
|
||||
{
|
||||
std::optional<std::string> load(const std::string& name);
|
||||
void store(const std::string& name, const std::string& value);
|
||||
}
|
102
src/utils/string.cpp
Normal file
102
src/utils/string.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#include "string.hpp"
|
||||
#include <sstream>
|
||||
#include <cstdarg>
|
||||
#include <algorithm>
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
const char* va(const char* fmt, ...)
|
||||
{
|
||||
static thread_local va_provider<8, 256> provider;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
const char* result = provider.get(fmt, ap);
|
||||
|
||||
va_end(ap);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& s, const char delim)
|
||||
{
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
std::vector<std::string> elems;
|
||||
|
||||
while (std::getline(ss, item, delim))
|
||||
{
|
||||
elems.push_back(std::move(item));
|
||||
item = std::string{};
|
||||
}
|
||||
|
||||
return elems;
|
||||
}
|
||||
|
||||
std::string to_lower(std::string text)
|
||||
{
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
|
||||
{
|
||||
return static_cast<char>(tolower(input));
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string to_upper(std::string text)
|
||||
{
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
|
||||
{
|
||||
return static_cast<char>(toupper(input));
|
||||
});
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
bool starts_with(const std::string& text, const std::string& substring)
|
||||
{
|
||||
return text.find(substring) == 0;
|
||||
}
|
||||
|
||||
bool ends_with(const std::string& text, const std::string& substring)
|
||||
{
|
||||
if (substring.size() > text.size()) return false;
|
||||
return std::equal(substring.rbegin(), substring.rend(), text.rbegin());
|
||||
}
|
||||
|
||||
std::string dump_hex(const std::string& data, const std::string& separator)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for (unsigned int i = 0; i < data.size(); ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
result.append(separator);
|
||||
}
|
||||
|
||||
result.append(va("%02X", data[i] & 0xFF));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string replace(std::string str, const std::string& from, const std::string& to)
|
||||
{
|
||||
if (from.empty())
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
std::size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
|
||||
{
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length();
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
97
src/utils/string.hpp
Normal file
97
src/utils/string.hpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
#include "memory.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
template <class Type, std::size_t n>
|
||||
constexpr std::size_t ARRAY_COUNT(Type(&)[n]) { return n; }
|
||||
|
||||
namespace utils::string
|
||||
{
|
||||
template <std::size_t buffers, std::size_t min_buffer_size>
|
||||
class va_provider final
|
||||
{
|
||||
public:
|
||||
static_assert(buffers != 0 && min_buffer_size != 0, "buffers and min_buffer_size mustn't be 0");
|
||||
|
||||
va_provider() : current_buffer_(0)
|
||||
{
|
||||
}
|
||||
|
||||
char* get(const char* format, va_list ap)
|
||||
{
|
||||
++this->current_buffer_ %= ARRAY_COUNT(this->string_pool_);
|
||||
auto entry = &this->string_pool_[this->current_buffer_];
|
||||
|
||||
if (!entry->size_ || !entry->buffer_)
|
||||
{
|
||||
throw std::runtime_error("String pool not initialized");
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const auto res = vsnprintf_s(entry->buffer_, entry->size_, _TRUNCATE, format, ap);
|
||||
#else
|
||||
const auto res = vsnprintf(entry->buffer_, entry->size_, format, ap);
|
||||
#endif
|
||||
|
||||
if (res > 0) break; // Success
|
||||
if (res == 0) return nullptr; // Error
|
||||
|
||||
entry->double_size();
|
||||
}
|
||||
|
||||
return entry->buffer_;
|
||||
}
|
||||
|
||||
private:
|
||||
class entry final
|
||||
{
|
||||
public:
|
||||
explicit entry(const std::size_t size = min_buffer_size) : size_(size), buffer_(nullptr)
|
||||
{
|
||||
if (this->size_ < min_buffer_size) this->size_ = min_buffer_size;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
~entry()
|
||||
{
|
||||
if (this->buffer_) memory::get_allocator()->free(this->buffer_);
|
||||
this->size_ = 0;
|
||||
this->buffer_ = nullptr;
|
||||
}
|
||||
|
||||
void allocate()
|
||||
{
|
||||
if (this->buffer_) memory::get_allocator()->free(this->buffer_);
|
||||
this->buffer_ = memory::get_allocator()->allocate_array<char>(this->size_ + 1);
|
||||
}
|
||||
|
||||
void double_size()
|
||||
{
|
||||
this->size_ *= 2;
|
||||
this->allocate();
|
||||
}
|
||||
|
||||
std::size_t size_;
|
||||
char* buffer_;
|
||||
};
|
||||
|
||||
std::size_t current_buffer_;
|
||||
entry string_pool_[buffers];
|
||||
};
|
||||
|
||||
const char* va(const char* fmt, ...);
|
||||
|
||||
std::vector<std::string> split(const std::string& s, char delim);
|
||||
|
||||
std::string to_lower(std::string text);
|
||||
std::string to_upper(std::string text);
|
||||
|
||||
bool starts_with(const std::string& text, const std::string& substring);
|
||||
bool ends_with(const std::string& text, const std::string& substring);
|
||||
|
||||
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||
|
||||
std::string replace(std::string str, const std::string& from, const std::string& to);
|
||||
}
|
Reference in New Issue
Block a user