mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-12 20:27:13 +02:00
feat: refactor
feat: use another JSON library fix: specify credits
This commit is contained in:
parent
90d62514e9
commit
f62eea3952
37 changed files with 26098 additions and 923 deletions
BIN
.github/AyuGram.png
vendored
BIN
.github/AyuGram.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 5.2 KiB |
14
README.md
14
README.md
|
@ -35,3 +35,17 @@ Make sure you have these components installed with VS Build Tools:
|
|||
- C++ MFC latest (x86 & x64)
|
||||
- C++ ATL latest (x86 & x64)
|
||||
- latest Windows 11 SDK
|
||||
|
||||
## Credits
|
||||
|
||||
- [Telegram Desktop](https://github.com/telegramdesktop/tdesktop)
|
||||
- [Kotatogram](https://github.com/kotatogram/kotatogram-desktop)
|
||||
- [64Gram](https://github.com/TDesktop-x64/tdesktop)
|
||||
- [SQLite](https://github.com/sqlite/sqlite)
|
||||
- [sqlite_orm](https://github.com/fnc12/sqlite_orm)
|
||||
|
||||
### Very special thanks to
|
||||
|
||||
- [JSON for Modern C++](https://github.com/nlohmann/json)
|
||||
- [BitConverter](https://github.com/YanjieHe/BitConverter)
|
||||
- [Not Enough Standards](https://github.com/Alairion/not-enough-standards)
|
||||
|
|
|
@ -95,35 +95,43 @@ nice_target_sources(Telegram ${src_loc}
|
|||
PRIVATE
|
||||
${style_files}
|
||||
|
||||
ayu/qserializer.h
|
||||
ayu/ayu_state.cpp
|
||||
ayu/ayu_state.h
|
||||
ayu/ayu_settings.cpp
|
||||
ayu/ayu_settings.h
|
||||
ayu/ayu_lang.cpp
|
||||
ayu/ayu_lang.h
|
||||
ayu/boxes/voice_confirmation_box.cpp
|
||||
ayu/boxes/voice_confirmation_box.h
|
||||
ayu/boxes/confirmation_box.cpp
|
||||
ayu/boxes/confirmation_box.h
|
||||
ayu/boxes/edit_deleted_mark.cpp
|
||||
ayu/boxes/edit_deleted_mark.h
|
||||
ayu/boxes/edit_edited_mark.cpp
|
||||
ayu/boxes/edit_edited_mark.h
|
||||
ayu/context_menu/context_menu.cpp
|
||||
ayu/context_menu/context_menu.h
|
||||
ayu/context_menu/message_history_box.cpp
|
||||
ayu/context_menu/message_history_box.h
|
||||
ayu/settings/settings_ayu.cpp
|
||||
ayu/settings/settings_ayu.h
|
||||
ayu/sqlite/sqlite3.c
|
||||
ayu/sqlite/sqlite3.h
|
||||
ayu/sqlite/sqlite_orm.h
|
||||
ayu/ui/utils/ayu_profile_values.cpp
|
||||
ayu/ui/utils/ayu_profile_values.h
|
||||
ayu/ui/settings/settings_ayu.cpp
|
||||
ayu/ui/settings/settings_ayu.h
|
||||
ayu/ui/context_menu/context_menu.cpp
|
||||
ayu/ui/context_menu/context_menu.h
|
||||
ayu/ui/context_menu/message_history_box.cpp
|
||||
ayu/ui/context_menu/message_history_box.h
|
||||
ayu/ui/boxes/voice_confirmation_box.cpp
|
||||
ayu/ui/boxes/voice_confirmation_box.h
|
||||
ayu/ui/boxes/confirmation_box.cpp
|
||||
ayu/ui/boxes/confirmation_box.h
|
||||
ayu/ui/boxes/edit_deleted_mark.cpp
|
||||
ayu/ui/boxes/edit_deleted_mark.h
|
||||
ayu/ui/boxes/edit_edited_mark.cpp
|
||||
ayu/ui/boxes/edit_edited_mark.h
|
||||
ayu/sync/ayu_sync_controller.cpp
|
||||
ayu/sync/ayu_sync_controller.h
|
||||
ayu/sync/models.h
|
||||
ayu/sync/utils/ayu_pipe_wrapper.cpp
|
||||
ayu/sync/utils/ayu_pipe_wrapper.h
|
||||
ayu/libs/pipe.hpp
|
||||
ayu/libs/json.hpp
|
||||
ayu/libs/json_ext.hpp
|
||||
ayu/libs/bit_converter.hpp
|
||||
ayu/libs/sqlite/sqlite3.c
|
||||
ayu/libs/sqlite/sqlite3.h
|
||||
ayu/libs/sqlite/sqlite_orm.h
|
||||
ayu/database/entities.h
|
||||
ayu/database/ayu_database.cpp
|
||||
ayu/database/ayu_database.h
|
||||
ayu/utils/ayu_profile_values.cpp
|
||||
ayu/utils/ayu_profile_values.h
|
||||
|
||||
api/api_attached_stickers.cpp
|
||||
api/api_attached_stickers.h
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "rpl/producer.h"
|
||||
#include "rpl/variable.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace AyuSettings {
|
||||
const QString filename = "tdata/ayu_settings.json";
|
||||
std::optional<AyuGramSettings> settings = std::nullopt;
|
||||
|
@ -66,22 +68,23 @@ namespace AyuSettings {
|
|||
return;
|
||||
}
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QByteArray json = file.readAll();
|
||||
QByteArray data = file.readAll();
|
||||
file.close();
|
||||
|
||||
initialize();
|
||||
settings->fromJson(json);
|
||||
json p = json::parse(data);
|
||||
settings = p.template get<AyuGramSettings>();
|
||||
postinitialize();
|
||||
}
|
||||
|
||||
void save() {
|
||||
initialize();
|
||||
|
||||
QByteArray json = settings->toRawJson();
|
||||
json p = settings.value();
|
||||
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(json);
|
||||
file.write(p.dump().c_str());
|
||||
file.close();
|
||||
|
||||
postinitialize();
|
||||
|
|
|
@ -8,16 +8,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "rpl/producer.h"
|
||||
|
||||
#define QS_HAS_JSON
|
||||
|
||||
#include "lang_auto.h"
|
||||
#include "qserializer.h"
|
||||
#include "ayu/libs/json.hpp"
|
||||
#include "ayu/libs/json_ext.hpp"
|
||||
|
||||
namespace AyuSettings {
|
||||
class AyuGramSettings : public QSerializer {
|
||||
Q_GADGET
|
||||
|
||||
class AyuGramSettings {
|
||||
public:
|
||||
AyuGramSettings() {
|
||||
// ~ Ghost essentials
|
||||
|
@ -56,75 +52,92 @@ namespace AyuSettings {
|
|||
showPeerId = 2;
|
||||
|
||||
showMessageSeconds = false;
|
||||
stickerConfirmation = false;
|
||||
GIFConfirmation = false;
|
||||
voiceConfirmation = false;
|
||||
}
|
||||
|
||||
QS_SERIALIZABLE
|
||||
|
||||
QS_FIELD(bool, sendReadPackets)
|
||||
|
||||
QS_FIELD(bool, sendOnlinePackets)
|
||||
|
||||
QS_FIELD(bool, sendUploadProgress)
|
||||
|
||||
QS_FIELD(bool, sendOfflinePacketAfterOnline)
|
||||
|
||||
QS_FIELD(bool, markReadAfterSend)
|
||||
|
||||
QS_FIELD(bool, useScheduledMessages)
|
||||
|
||||
QS_FIELD(bool, keepDeletedMessages)
|
||||
|
||||
QS_FIELD(bool, keepMessagesHistory)
|
||||
|
||||
QS_FIELD(bool, enableAds)
|
||||
|
||||
QS_FIELD(QString, deletedMark)
|
||||
|
||||
QS_FIELD(QString, editedMark)
|
||||
|
||||
QS_FIELD(int, recentStickersCount)
|
||||
|
||||
QS_FIELD(bool, showGhostToggleInDrawer)
|
||||
|
||||
QS_FIELD(int, showPeerId)
|
||||
|
||||
QS_FIELD(bool, showMessageSeconds)
|
||||
|
||||
QS_FIELD(bool, stickerConfirmation)
|
||||
|
||||
QS_FIELD(bool, GIFConfirmation)
|
||||
|
||||
QS_FIELD(bool, voiceConfirmation)
|
||||
bool sendReadPackets;
|
||||
bool sendOnlinePackets;
|
||||
bool sendUploadProgress;
|
||||
bool sendOfflinePacketAfterOnline;
|
||||
bool markReadAfterSend;
|
||||
bool useScheduledMessages;
|
||||
bool keepDeletedMessages;
|
||||
bool keepMessagesHistory;
|
||||
bool enableAds;
|
||||
QString deletedMark;
|
||||
QString editedMark;
|
||||
int recentStickersCount;
|
||||
bool showGhostToggleInDrawer;
|
||||
int showPeerId;
|
||||
bool showMessageSeconds;
|
||||
bool stickerConfirmation;
|
||||
bool GIFConfirmation;
|
||||
bool voiceConfirmation;
|
||||
|
||||
public:
|
||||
void set_sendReadPackets(bool val);
|
||||
|
||||
void set_sendOnlinePackets(bool val);
|
||||
|
||||
void set_sendUploadProgress(bool val);
|
||||
|
||||
void set_sendOfflinePacketAfterOnline(bool val);
|
||||
|
||||
void set_markReadAfterSend(bool val);
|
||||
|
||||
void set_useScheduledMessages(bool val);
|
||||
|
||||
void set_keepDeletedMessages(bool val);
|
||||
|
||||
void set_keepMessagesHistory(bool val);
|
||||
|
||||
void set_enableAds(bool val);
|
||||
|
||||
void set_deletedMark(QString val);
|
||||
|
||||
void set_editedMark(QString val);
|
||||
|
||||
void set_recentStickersCount(int val);
|
||||
|
||||
void set_showGhostToggleInDrawer(bool val);
|
||||
|
||||
void set_showPeerId(int val);
|
||||
|
||||
void set_showMessageSeconds(bool val);
|
||||
|
||||
void set_stickerConfirmation(bool val);
|
||||
|
||||
void set_GIFConfirmation(bool val);
|
||||
|
||||
void set_voiceConfirmation(bool val);
|
||||
|
||||
bool get_ghostModeEnabled() const;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
|
||||
AyuGramSettings,
|
||||
sendReadPackets,
|
||||
sendOnlinePackets,
|
||||
sendUploadProgress,
|
||||
sendOfflinePacketAfterOnline,
|
||||
markReadAfterSend,
|
||||
useScheduledMessages,
|
||||
keepDeletedMessages,
|
||||
keepMessagesHistory,
|
||||
enableAds,
|
||||
deletedMark,
|
||||
editedMark,
|
||||
recentStickersCount,
|
||||
showGhostToggleInDrawer,
|
||||
showPeerId,
|
||||
showMessageSeconds,
|
||||
stickerConfirmation,
|
||||
GIFConfirmation,
|
||||
voiceConfirmation
|
||||
);
|
||||
|
||||
AyuGramSettings &getInstance();
|
||||
|
||||
void load();
|
||||
|
@ -132,7 +145,9 @@ namespace AyuSettings {
|
|||
void save();
|
||||
|
||||
rpl::producer<QString> get_deletedMarkReactive();
|
||||
|
||||
rpl::producer<QString> get_editedMarkReactive();
|
||||
|
||||
rpl::producer<int> get_showPeerIdReactive();
|
||||
|
||||
// computed fields
|
||||
|
|
368
Telegram/SourceFiles/ayu/libs/bit_converter.hpp
Normal file
368
Telegram/SourceFiles/ayu/libs/bit_converter.hpp
Normal file
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Yanjie He
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace bit_converter {
|
||||
|
||||
using std::vector;
|
||||
|
||||
/**
|
||||
* @brief Convert the specified 16-bit signed integer value to bytes.
|
||||
*/
|
||||
template <typename OutputIt>
|
||||
inline OutputIt i16_to_bytes(int16_t value, bool is_big_endian,
|
||||
OutputIt output_it) {
|
||||
if (is_big_endian) {
|
||||
*output_it = static_cast<uint8_t>(value >> 8);
|
||||
output_it++;
|
||||
*output_it = static_cast<uint8_t>(value & 0xFF);
|
||||
output_it++;
|
||||
} else {
|
||||
*output_it = static_cast<uint8_t>(value & 0xFF);
|
||||
output_it++;
|
||||
*output_it = static_cast<uint8_t>(value >> 8);
|
||||
output_it++;
|
||||
}
|
||||
return output_it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert the specified 16-bit unsigned integer value to bytes.
|
||||
*/
|
||||
template <typename OutputIt>
|
||||
inline OutputIt u16_to_bytes(uint16_t value, bool is_big_endian,
|
||||
OutputIt output_it) {
|
||||
return i16_to_bytes(static_cast<uint16_t>(value), is_big_endian, output_it);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert the specified 32-bit signed integer value to bytes.
|
||||
*/
|
||||
template <typename OutputIt>
|
||||
inline OutputIt i32_to_bytes(int32_t value, bool is_big_endian,
|
||||
OutputIt output_it) {
|
||||
if (is_big_endian) {
|
||||
for (int i = 0; i < static_cast<int>(sizeof(int32_t)); i++) {
|
||||
*output_it = static_cast<uint8_t>((value >> (24 - i * 8)) & 0xFF);
|
||||
output_it++;
|
||||
}
|
||||
} else {
|
||||
for (int i = static_cast<int>(sizeof(int32_t)) - 1; i >= 0; i--) {
|
||||
*output_it = static_cast<uint8_t>((value >> (24 - i * 8)) & 0xFF);
|
||||
output_it++;
|
||||
}
|
||||
}
|
||||
return output_it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert the specified 32-bit unsigned integer value to bytes.
|
||||
*/
|
||||
template <typename OutputIt>
|
||||
inline OutputIt u32_to_bytes(uint32_t value, bool is_big_endian,
|
||||
OutputIt output_it) {
|
||||
return i32_to_bytes(static_cast<int32_t>(value), is_big_endian, output_it);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert the specified 64-bit signed integer value to bytes.
|
||||
*/
|
||||
template <typename OutputIt>
|
||||
inline OutputIt i64_to_bytes(int64_t value, bool is_big_endian,
|
||||
OutputIt output_it) {
|
||||
if (is_big_endian) {
|
||||
for (int i = 0; i < static_cast<int>(sizeof(int64_t)); i++) {
|
||||
*output_it = static_cast<uint8_t>((value >> (56 - i * 8)) & 0xFF);
|
||||
output_it++;
|
||||
}
|
||||
} else {
|
||||
for (int i = static_cast<int>(sizeof(int64_t)) - 1; i >= 0; i--) {
|
||||
*output_it = static_cast<uint8_t>((value >> (56 - i * 8)) & 0xFF);
|
||||
output_it++;
|
||||
}
|
||||
}
|
||||
return output_it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert the specified 64-bit unsigned integer value to bytes.
|
||||
*/
|
||||
template <typename OutputIt>
|
||||
inline OutputIt u64_to_bytes(uint64_t value, bool is_big_endian,
|
||||
OutputIt output_it) {
|
||||
return i64_to_bytes(static_cast<int64_t>(value), is_big_endian, output_it);
|
||||
}
|
||||
|
||||
template <typename OutputIt>
|
||||
inline OutputIt create_bytes_from_bits(const vector<bool> &bits,
|
||||
OutputIt output_it) {
|
||||
for (int i = 0; i < static_cast<int>(bits.size() / 8); i++) {
|
||||
uint8_t b = 0;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
b = b + (bits[i * 8 + j] << j);
|
||||
}
|
||||
*output_it = b;
|
||||
output_it++;
|
||||
}
|
||||
return output_it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert the specified single-precision floating-point value to bytes.
|
||||
*/
|
||||
template <typename OutputIt>
|
||||
inline OutputIt f32_to_bytes(float_t value, bool is_big_endian,
|
||||
OutputIt output_it) {
|
||||
vector<bool> bits;
|
||||
bits.reserve(sizeof(float_t) * 8);
|
||||
bits.push_back(value < 0);
|
||||
|
||||
int exponent;
|
||||
float_t mantissa = std::frexp(value, &exponent);
|
||||
exponent = (exponent - 1) + 127;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bits.push_back(exponent % 2);
|
||||
exponent = exponent / 2;
|
||||
}
|
||||
std::reverse(bits.begin() + 1, bits.begin() + 1 + 8);
|
||||
mantissa = mantissa * 2 - 1;
|
||||
for (int i = 0; i < 23; i++) {
|
||||
mantissa = mantissa * 2;
|
||||
if (mantissa >= 1.0) {
|
||||
mantissa = mantissa - 1.0;
|
||||
bits.push_back(true);
|
||||
} else {
|
||||
bits.push_back(false);
|
||||
}
|
||||
}
|
||||
if (is_big_endian) {
|
||||
return create_bytes_from_bits(bits, output_it);
|
||||
} else {
|
||||
std::reverse(bits.begin(), bits.end());
|
||||
return create_bytes_from_bits(bits, output_it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert the specified double-precision floating-point value to bytes.
|
||||
*/
|
||||
template <typename OutputIt>
|
||||
inline OutputIt f64_to_bytes(double_t value, bool is_big_endian,
|
||||
OutputIt output_it) {
|
||||
vector<bool> bits;
|
||||
bits.reserve(sizeof(double_t) * 8);
|
||||
bits.push_back(value < 0);
|
||||
|
||||
int exponent;
|
||||
double_t mantissa = std::frexp(value, &exponent);
|
||||
exponent = (exponent - 1) + 1023;
|
||||
|
||||
for (int i = 0; i < 11; i++) {
|
||||
bits.push_back(exponent % 2);
|
||||
exponent = exponent / 2;
|
||||
}
|
||||
std::reverse(bits.begin() + 1, bits.begin() + 1 + 11);
|
||||
mantissa = mantissa * 2 - 1;
|
||||
for (int i = 0; i < 52; i++) {
|
||||
mantissa = mantissa * 2;
|
||||
if (mantissa >= 1.0) {
|
||||
mantissa = mantissa - 1.0;
|
||||
bits.push_back(true);
|
||||
} else {
|
||||
bits.push_back(false);
|
||||
}
|
||||
}
|
||||
if (is_big_endian) {
|
||||
return create_bytes_from_bits(bits, output_it);
|
||||
} else {
|
||||
std::reverse(bits.begin(), bits.end());
|
||||
return create_bytes_from_bits(bits, output_it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a 16-bit signed integer converted from two bytes at a
|
||||
* specified position in a byte sequence.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
inline int16_t bytes_to_i16(InputIt input_it, bool is_big_endian) {
|
||||
if (is_big_endian) {
|
||||
int16_t result = *input_it;
|
||||
input_it++;
|
||||
result = (result << 8) + *input_it;
|
||||
input_it++;
|
||||
return result;
|
||||
} else {
|
||||
int16_t result = *input_it;
|
||||
input_it++;
|
||||
result = result + (static_cast<int16_t>(*input_it) << 8);
|
||||
input_it++;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a 16-bit unsigned integer converted from two bytes at a
|
||||
* specified position in a byte sequence.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
inline uint16_t bytes_to_u16(InputIt input_it, bool is_big_endian) {
|
||||
return static_cast<uint16_t>(bytes_to_i16(input_it, is_big_endian));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a 32-bit signed integer converted from four bytes at a
|
||||
* specified position in a byte sequence.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
inline int32_t bytes_to_i32(InputIt input_it, bool is_big_endian) {
|
||||
int32_t result = 0;
|
||||
if (is_big_endian) {
|
||||
for (int i = 0; i < static_cast<int>(sizeof(int32_t)); i++) {
|
||||
result = result + ((*input_it) << (8 * (4 - 1 - i)));
|
||||
input_it++;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
for (int i = 0; i < static_cast<int>(sizeof(int32_t)); i++) {
|
||||
result = result + ((*input_it) << (8 * i));
|
||||
input_it++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a 32-bit unsigned integer converted from four bytes at a
|
||||
* specified position in a byte sequence.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
inline uint32_t bytes_to_u32(InputIt input_it, bool is_big_endian) {
|
||||
return static_cast<uint32_t>(bytes_to_i32(input_it, is_big_endian));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a 64-bit signed integer converted from eight bytes at a
|
||||
* specified position in a byte sequence.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
inline int64_t bytes_to_i64(InputIt input_it, bool is_big_endian) {
|
||||
int64_t result = 0;
|
||||
if (is_big_endian) {
|
||||
for (int i = 0; i < static_cast<int>(sizeof(int64_t)); i++) {
|
||||
result = result + (static_cast<int64_t>(*input_it) << (8 * (8 - 1 - i)));
|
||||
input_it++;
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
for (int i = 0; i < static_cast<int>(sizeof(int64_t)); i++) {
|
||||
result = result + (static_cast<int64_t>(*input_it) << (8 * i));
|
||||
input_it++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a 64-bit unsigned integer converted from eight bytes at a
|
||||
* specified position in a byte sequence.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
inline uint64_t bytes_to_u64(InputIt input_it, bool is_big_endian) {
|
||||
return static_cast<uint64_t>(input_it, is_big_endian);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a single-precision floating-point number converted from four
|
||||
* bytes at a specified position in a byte sequence.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
inline float_t bytes_to_f32(InputIt input_it, bool is_big_endian) {
|
||||
vector<bool> bits;
|
||||
bits.reserve(sizeof(float_t) * 8);
|
||||
for (int i = 0; i < static_cast<int>(sizeof(float_t)); i++) {
|
||||
uint8_t b = *input_it;
|
||||
input_it++;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
bits.push_back(b % 2);
|
||||
b = b / 2;
|
||||
}
|
||||
}
|
||||
if (!is_big_endian) {
|
||||
std::reverse(bits.begin(), bits.end());
|
||||
}
|
||||
int sign = bits[0] ? (-1) : (+1);
|
||||
int exponent = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
exponent = exponent + bits[1 + i] * (1 << (8 - 1 - i));
|
||||
}
|
||||
exponent = exponent - 127;
|
||||
float_t mantissa = 1.0;
|
||||
float_t cur = 0.5;
|
||||
for (int i = 0; i < 23; i++) {
|
||||
mantissa = mantissa + bits[1 + 8 + i] * cur;
|
||||
cur = cur / 2;
|
||||
}
|
||||
return sign * std::ldexp(mantissa, exponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a double-precision floating-point number converted from eight
|
||||
* bytes at a specified position in a byte sequence.
|
||||
*/
|
||||
template <typename InputIt>
|
||||
inline double_t bytes_to_f64(InputIt input_it, bool is_big_endian) {
|
||||
vector<bool> bits;
|
||||
bits.reserve(sizeof(double_t) * 8);
|
||||
for (int i = 0; i < static_cast<int>(sizeof(double_t)); i++) {
|
||||
uint8_t b = *input_it;
|
||||
input_it++;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
bits.push_back(b % 2);
|
||||
b = b / 2;
|
||||
}
|
||||
}
|
||||
if (!is_big_endian) {
|
||||
std::reverse(bits.begin(), bits.end());
|
||||
}
|
||||
int sign = bits[0] ? (-1) : (+1);
|
||||
int exponent = 0;
|
||||
for (int i = 0; i < 11; i++) {
|
||||
exponent = exponent + bits[1 + i] * (1 << (11 - 1 - i));
|
||||
}
|
||||
exponent = exponent - 1023;
|
||||
double_t mantissa = 1.0;
|
||||
double_t cur = 0.5;
|
||||
for (int i = 0; i < 52; i++) {
|
||||
mantissa = mantissa + bits[1 + 11 + i] * cur;
|
||||
cur = cur / 2;
|
||||
}
|
||||
return sign * std::ldexp(mantissa, exponent);
|
||||
}
|
||||
|
||||
}; // namespace bit_converter
|
24596
Telegram/SourceFiles/ayu/libs/json.hpp
Normal file
24596
Telegram/SourceFiles/ayu/libs/json.hpp
Normal file
File diff suppressed because it is too large
Load diff
19
Telegram/SourceFiles/ayu/libs/json_ext.hpp
Normal file
19
Telegram/SourceFiles/ayu/libs/json_ext.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include "json.hpp"
|
||||
|
||||
inline void to_json(nlohmann::json &j, const QString &q) {
|
||||
j = nlohmann::json(q.toStdString());
|
||||
}
|
||||
|
||||
inline void from_json(const nlohmann::json &j, QString &q) {
|
||||
q = QString::fromStdString(j.get<std::string>());
|
||||
}
|
856
Telegram/SourceFiles/ayu/libs/pipe.hpp
Normal file
856
Telegram/SourceFiles/ayu/libs/pipe.hpp
Normal file
|
@ -0,0 +1,856 @@
|
|||
///////////////////////////////////////////////////////////
|
||||
/// Copyright 2019 Alexy Pellegrini
|
||||
///
|
||||
/// Permission is hereby granted, free of charge,
|
||||
/// to any person obtaining a copy of this software
|
||||
/// and associated documentation files (the "Software"),
|
||||
/// to deal in the Software without restriction,
|
||||
/// including without limitation the rights to use,
|
||||
/// copy, modify, merge, publish, distribute, sublicense,
|
||||
/// and/or sell copies of the Software, and to permit
|
||||
/// persons to whom the Software is furnished to do so,
|
||||
/// subject to the following conditions:
|
||||
///
|
||||
/// The above copyright notice and this permission notice
|
||||
/// shall be included in all copies or substantial portions
|
||||
/// of the Software.
|
||||
///
|
||||
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
/// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
/// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
/// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
/// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
/// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
/// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
/// OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef NOT_ENOUGH_STANDARDS_PIPE
|
||||
#define NOT_ENOUGH_STANDARDS_PIPE
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define NES_WIN32_PIPE
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
|
||||
#define NES_POSIX_PIPE
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#else
|
||||
#error "Not enough standards does not support this environment."
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <streambuf>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
#if defined(NES_WIN32_PIPE)
|
||||
|
||||
namespace nes
|
||||
{
|
||||
|
||||
inline constexpr const char pipe_root[] = "\\\\.\\pipe\\";
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
class basic_pipe_istream;
|
||||
template<typename CharT, typename Traits>
|
||||
class basic_pipe_ostream;
|
||||
template<typename CharT = char, typename Traits = std::char_traits<CharT>>
|
||||
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();
|
||||
|
||||
template<typename CharT, typename Traits = std::char_traits<CharT>>
|
||||
class basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>
|
||||
{
|
||||
private:
|
||||
using parent_type = std::basic_streambuf<CharT, Traits>;
|
||||
|
||||
public:
|
||||
using char_type = CharT;
|
||||
using traits_type = Traits;
|
||||
using int_type = typename Traits::int_type;
|
||||
using pos_type = typename Traits::pos_type;
|
||||
using off_type = typename Traits::off_type;
|
||||
|
||||
public:
|
||||
static constexpr std::size_t buf_size{1024};
|
||||
|
||||
public:
|
||||
basic_pipe_streambuf() = default;
|
||||
|
||||
explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)
|
||||
{
|
||||
open(name, mode);
|
||||
}
|
||||
|
||||
virtual ~basic_pipe_streambuf()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;
|
||||
basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;
|
||||
|
||||
basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept
|
||||
:parent_type{other}
|
||||
,m_buffer{std::move(other.m_buffer)}
|
||||
,m_handle{std::exchange(other.m_handle, INVALID_HANDLE_VALUE)}
|
||||
,m_mode{std::exchange(other.m_mode, std::ios_base::openmode{})}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept
|
||||
{
|
||||
parent_type::operator=(other);
|
||||
m_buffer = std::move(other.m_buffer);
|
||||
m_handle = std::exchange(other.m_handle, m_handle);
|
||||
m_mode = std::exchange(other.m_mode, m_mode);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool open(const std::string& name, std::ios_base::openmode mode)
|
||||
{
|
||||
assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) && "nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.");
|
||||
|
||||
close();
|
||||
|
||||
const auto native_name{to_wide(pipe_root + name)};
|
||||
DWORD native_mode{mode & std::ios_base::in ? GENERIC_READ : GENERIC_WRITE};
|
||||
|
||||
HANDLE handle = CreateFileW(std::data(native_name), native_mode, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if(GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||
{
|
||||
native_mode = mode & std::ios_base::in ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
|
||||
|
||||
handle = CreateNamedPipeW(std::data(native_name), native_mode, PIPE_READMODE_BYTE | PIPE_WAIT, 1, buf_size, buf_size, 0, nullptr);
|
||||
if(handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
if(!ConnectNamedPipe(handle, nullptr))
|
||||
{
|
||||
CloseHandle(handle);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer.resize(buf_size);
|
||||
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
|
||||
m_handle = handle;
|
||||
m_mode = mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_open() const noexcept
|
||||
{
|
||||
return m_handle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if(is_open())
|
||||
{
|
||||
sync();
|
||||
|
||||
m_mode = std::ios_base::openmode{};
|
||||
CloseHandle(std::exchange(m_handle, INVALID_HANDLE_VALUE));
|
||||
parent_type::setp(nullptr, nullptr);
|
||||
parent_type::setg(nullptr, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class process;
|
||||
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
|
||||
|
||||
basic_pipe_streambuf(HANDLE handle, std::ios_base::openmode mode)
|
||||
:m_handle{handle}
|
||||
,m_mode{mode}
|
||||
{
|
||||
m_buffer.resize(buf_size);
|
||||
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int sync() override
|
||||
{
|
||||
if(m_mode & std::ios_base::out)
|
||||
{
|
||||
const std::ptrdiff_t count{parent_type::pptr() - parent_type::pbase()};
|
||||
|
||||
DWORD written{};
|
||||
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(count)* sizeof(char_type), &written, nullptr))
|
||||
return -1;
|
||||
|
||||
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual int_type overflow(int_type c = traits_type::eof()) override
|
||||
{
|
||||
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
|
||||
|
||||
if(traits_type::eq_int_type(c, traits_type::eof()))
|
||||
{
|
||||
DWORD written{};
|
||||
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(std::data(m_buffer)), static_cast<DWORD>(buf_size) * sizeof(char_type), &written, nullptr))
|
||||
return traits_type::eof();
|
||||
|
||||
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
*parent_type::pptr() = traits_type::to_char_type(c);
|
||||
parent_type::pbump(1);
|
||||
}
|
||||
|
||||
return traits_type::not_eof(c);
|
||||
}
|
||||
|
||||
virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override
|
||||
{
|
||||
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
|
||||
|
||||
DWORD written{};
|
||||
if(!WriteFile(m_handle, reinterpret_cast<const CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &written, nullptr))
|
||||
return 0;
|
||||
|
||||
return static_cast<std::streamsize>(written);
|
||||
}
|
||||
|
||||
virtual int_type underflow() override
|
||||
{
|
||||
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
|
||||
|
||||
if(parent_type::gptr() == parent_type::egptr())
|
||||
{
|
||||
DWORD readed{};
|
||||
if(!ReadFile(m_handle, reinterpret_cast<CHAR*>(std::data(m_buffer)), static_cast<DWORD>(buf_size * sizeof(char_type)), &readed, nullptr) || readed == 0)
|
||||
return traits_type::eof();
|
||||
|
||||
parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed / sizeof(char_type)));
|
||||
}
|
||||
|
||||
return traits_type::to_int_type(*parent_type::gptr());
|
||||
}
|
||||
|
||||
virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
|
||||
{
|
||||
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
|
||||
|
||||
DWORD readed{};
|
||||
if(!ReadFile(m_handle, reinterpret_cast<CHAR*>(s), static_cast<DWORD>(count) * sizeof(char_type), &readed, nullptr))
|
||||
return 0;
|
||||
|
||||
return static_cast<std::streamsize>(readed / sizeof(char_type));
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring to_wide(std::string path)
|
||||
{
|
||||
assert(std::size(path) < 0x7FFFFFFFu && "Wrong path.");
|
||||
|
||||
if(std::empty(path))
|
||||
return {};
|
||||
|
||||
std::transform(std::begin(path), std::end(path), std::begin(path), [](char c){return c == '/' ? '\\' : c;});
|
||||
|
||||
std::wstring out_path{};
|
||||
out_path.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), nullptr, 0)));
|
||||
|
||||
if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, std::data(path), static_cast<int>(std::size(path)), std::data(out_path), static_cast<int>(std::size(out_path))))
|
||||
throw std::runtime_error{"Failed to convert the path to wide."};
|
||||
|
||||
return out_path;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<CharT> m_buffer{};
|
||||
HANDLE m_handle{INVALID_HANDLE_VALUE};
|
||||
std::ios_base::openmode m_mode{};
|
||||
};
|
||||
|
||||
|
||||
template<typename CharT, typename Traits = std::char_traits<CharT>>
|
||||
class basic_pipe_istream : public std::basic_istream<CharT, Traits>
|
||||
{
|
||||
private:
|
||||
using parent_type = std::basic_istream<CharT, Traits>;
|
||||
|
||||
public:
|
||||
using char_type = CharT;
|
||||
using traits_type = Traits;
|
||||
using int_type = typename Traits::int_type;
|
||||
using pos_type = typename Traits::pos_type;
|
||||
using off_type = typename Traits::off_type;
|
||||
|
||||
public:
|
||||
basic_pipe_istream() = default;
|
||||
|
||||
explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
|
||||
:parent_type{nullptr}
|
||||
{
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
open(name, mode);
|
||||
}
|
||||
|
||||
virtual ~basic_pipe_istream() = default;
|
||||
|
||||
basic_pipe_istream(const basic_pipe_istream&) = delete;
|
||||
basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;
|
||||
|
||||
basic_pipe_istream(basic_pipe_istream&& other) noexcept
|
||||
:parent_type{std::move(other)}
|
||||
{
|
||||
std::swap(m_buffer, other.m_buffer);
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
}
|
||||
|
||||
basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept
|
||||
{
|
||||
parent_type::operator=(std::move(other));
|
||||
std::swap(m_buffer, other.m_buffer);
|
||||
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
|
||||
{
|
||||
m_buffer->open(name, mode);
|
||||
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
|
||||
}
|
||||
|
||||
bool is_open() const noexcept
|
||||
{
|
||||
return m_buffer->is_open();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
m_buffer->close();
|
||||
}
|
||||
|
||||
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
|
||||
{
|
||||
return m_buffer.get();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class process;
|
||||
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
|
||||
|
||||
basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)
|
||||
:parent_type{nullptr}
|
||||
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
|
||||
{
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
|
||||
};
|
||||
|
||||
template<typename CharT, typename Traits = std::char_traits<CharT>>
|
||||
class basic_pipe_ostream : public std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
private:
|
||||
using parent_type = std::basic_ostream<CharT, Traits>;
|
||||
|
||||
public:
|
||||
using char_type = CharT;
|
||||
using traits_type = Traits;
|
||||
using int_type = typename Traits::int_type;
|
||||
using pos_type = typename Traits::pos_type;
|
||||
using off_type = typename Traits::off_type;
|
||||
|
||||
public:
|
||||
basic_pipe_ostream() = default;
|
||||
|
||||
explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
|
||||
:parent_type{nullptr}
|
||||
{
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
open(name, mode);
|
||||
}
|
||||
|
||||
virtual ~basic_pipe_ostream() = default;
|
||||
|
||||
basic_pipe_ostream(const basic_pipe_ostream&) = delete;
|
||||
basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;
|
||||
|
||||
basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
|
||||
:parent_type{std::move(other)}
|
||||
{
|
||||
std::swap(m_buffer, other.m_buffer);
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
}
|
||||
|
||||
basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept
|
||||
{
|
||||
parent_type::operator=(std::move(other));
|
||||
std::swap(m_buffer, other.m_buffer);
|
||||
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
|
||||
{
|
||||
m_buffer->open(name, mode);
|
||||
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
|
||||
}
|
||||
|
||||
bool is_open() const noexcept
|
||||
{
|
||||
return m_buffer->is_open();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
m_buffer->close();
|
||||
}
|
||||
|
||||
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
|
||||
{
|
||||
return m_buffer.get();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class process;
|
||||
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
|
||||
|
||||
basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)
|
||||
:parent_type{nullptr}
|
||||
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
|
||||
{
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
|
||||
};
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
|
||||
{
|
||||
HANDLE input{};
|
||||
HANDLE output{};
|
||||
|
||||
if(!CreatePipe(&input, &output, nullptr, 0))
|
||||
throw std::runtime_error{"Failed to create pipe"};
|
||||
|
||||
return std::make_pair(basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{input, std::ios_base::in}},
|
||||
basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{output, std::ios_base::out}});
|
||||
}
|
||||
|
||||
using pipe_streambuf = basic_pipe_streambuf<char>;
|
||||
using pipe_istream = basic_pipe_istream<char>;
|
||||
using pipe_ostream = basic_pipe_ostream<char>;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(NES_POSIX_PIPE)
|
||||
|
||||
|
||||
namespace nes
|
||||
{
|
||||
|
||||
inline constexpr const char pipe_root[] = "/tmp/";
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
class basic_pipe_istream;
|
||||
template<typename CharT, typename Traits>
|
||||
class basic_pipe_ostream;
|
||||
template<typename CharT = char, typename Traits = std::char_traits<CharT>>
|
||||
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe();
|
||||
|
||||
template<typename CharT, typename Traits = std::char_traits<CharT>>
|
||||
class basic_pipe_streambuf : public std::basic_streambuf<CharT, Traits>
|
||||
{
|
||||
private:
|
||||
using parent_type = std::basic_streambuf<CharT, Traits>;
|
||||
|
||||
public:
|
||||
using char_type = CharT;
|
||||
using traits_type = Traits;
|
||||
using int_type = typename Traits::int_type;
|
||||
using pos_type = typename Traits::pos_type;
|
||||
using off_type = typename Traits::off_type;
|
||||
|
||||
public:
|
||||
static constexpr std::size_t buf_size{1024};
|
||||
|
||||
public:
|
||||
basic_pipe_streambuf() = default;
|
||||
|
||||
explicit basic_pipe_streambuf(const std::string& name, std::ios_base::openmode mode)
|
||||
:parent_type{nullptr}
|
||||
{
|
||||
open(name, mode);
|
||||
}
|
||||
|
||||
virtual ~basic_pipe_streambuf()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
basic_pipe_streambuf(const basic_pipe_streambuf&) = delete;
|
||||
basic_pipe_streambuf& operator=(const basic_pipe_streambuf&) = delete;
|
||||
|
||||
basic_pipe_streambuf(basic_pipe_streambuf&& other) noexcept
|
||||
:parent_type{std::move(other)}
|
||||
,m_buffer{std::move(other.m_buffer)}
|
||||
,m_handle{std::exchange(other.m_handle, 0)}
|
||||
,m_mode{std::exchange(other.m_mode, std::ios_base::openmode{})}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
basic_pipe_streambuf& operator=(basic_pipe_streambuf&& other) noexcept
|
||||
{
|
||||
parent_type::operator=(std::move(other));
|
||||
m_buffer = std::move(other.m_buffer);
|
||||
m_handle = std::exchange(other.m_handle, m_handle);
|
||||
m_mode = std::exchange(other.m_mode, m_mode);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool open(const std::string& name, std::ios_base::openmode mode)
|
||||
{
|
||||
assert(!((mode & std::ios_base::in) && (mode & std::ios_base::out)) && "nes::basic_pipe_streambuf::open called with mode = std::ios_base::in | std::ios_base::out.");
|
||||
|
||||
close();
|
||||
|
||||
const auto native_name{pipe_root + name};
|
||||
if(mkfifo(std::data(native_name), 0660) != 0 && errno != EEXIST)
|
||||
return false;
|
||||
|
||||
const int native_mode{mode & std::ios_base::in ? O_RDONLY : O_WRONLY};
|
||||
int handle = ::open(std::data(native_name), native_mode);
|
||||
if(handle < 0)
|
||||
return false;
|
||||
|
||||
m_buffer.resize(buf_size);
|
||||
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
|
||||
m_handle = handle;
|
||||
m_mode = mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_open() const noexcept
|
||||
{
|
||||
return m_handle;
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if(is_open())
|
||||
{
|
||||
sync();
|
||||
|
||||
m_mode = std::ios_base::openmode{};
|
||||
::close(std::exchange(m_handle, 0));
|
||||
parent_type::setp(nullptr, nullptr);
|
||||
parent_type::setg(nullptr, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
friend class process;
|
||||
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
|
||||
|
||||
basic_pipe_streambuf(int handle, std::ios_base::openmode mode)
|
||||
:m_handle{handle}
|
||||
,m_mode{mode}
|
||||
{
|
||||
m_buffer.resize(buf_size);
|
||||
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int sync() override
|
||||
{
|
||||
if(m_mode & std::ios_base::out)
|
||||
{
|
||||
const std::ptrdiff_t count{parent_type::pptr() - parent_type::pbase()};
|
||||
|
||||
if(write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), count * sizeof(char_type)) < 0)
|
||||
return -1;
|
||||
|
||||
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual int_type overflow(int_type c = traits_type::eof()) override
|
||||
{
|
||||
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
|
||||
|
||||
if(traits_type::eq_int_type(c, traits_type::eof()))
|
||||
{
|
||||
if(write(m_handle, reinterpret_cast<const char*>(std::data(m_buffer)), std::size(m_buffer) * sizeof(char_type)))
|
||||
return traits_type::eof();
|
||||
|
||||
parent_type::setp(std::data(m_buffer), std::data(m_buffer) + buf_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
*parent_type::pptr() = traits_type::to_char_type(c);
|
||||
parent_type::pbump(1);
|
||||
}
|
||||
|
||||
return traits_type::not_eof(c);
|
||||
}
|
||||
|
||||
virtual std::streamsize xsputn(const char_type* s, std::streamsize count) override
|
||||
{
|
||||
assert(m_mode & std::ios_base::out && "Write operation on a read only pipe.");
|
||||
|
||||
const auto written = write(m_handle, reinterpret_cast<const char*>(s), count * sizeof(char_type));
|
||||
if(written < 0)
|
||||
return 0;
|
||||
|
||||
return static_cast<std::streamsize>(written);
|
||||
}
|
||||
|
||||
virtual int_type underflow() override
|
||||
{
|
||||
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
|
||||
|
||||
if(parent_type::gptr() == parent_type::egptr())
|
||||
{
|
||||
const auto readed = read(m_handle, reinterpret_cast<char*>(std::data(m_buffer)), buf_size * sizeof(char_type));
|
||||
if(readed <= 0)
|
||||
return traits_type::eof();
|
||||
|
||||
parent_type::setg(std::data(m_buffer), std::data(m_buffer), std::data(m_buffer) + (readed / sizeof(char_type)));
|
||||
}
|
||||
|
||||
return traits_type::to_int_type(*parent_type::gptr());
|
||||
}
|
||||
|
||||
virtual std::streamsize xsgetn(char_type* s, std::streamsize count) override
|
||||
{
|
||||
assert(m_mode & std::ios_base::in && "Read operation on a write only pipe.");
|
||||
|
||||
const auto readed = read(m_handle, reinterpret_cast<char*>(s), count * sizeof(char_type));
|
||||
if(readed < 0)
|
||||
return 0;
|
||||
|
||||
return static_cast<std::streamsize>(readed / sizeof(char_type));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<CharT> m_buffer{};
|
||||
int m_handle{};
|
||||
std::ios_base::openmode m_mode{};
|
||||
};
|
||||
|
||||
template<typename CharT, typename Traits = std::char_traits<CharT>>
|
||||
class basic_pipe_istream : public std::basic_istream<CharT, Traits>
|
||||
{
|
||||
private:
|
||||
using parent_type = std::basic_istream<CharT, Traits>;
|
||||
|
||||
public:
|
||||
using char_type = CharT;
|
||||
using traits_type = Traits;
|
||||
using int_type = typename Traits::int_type;
|
||||
using pos_type = typename Traits::pos_type;
|
||||
using off_type = typename Traits::off_type;
|
||||
|
||||
public:
|
||||
basic_pipe_istream() = default;
|
||||
|
||||
explicit basic_pipe_istream(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
|
||||
:parent_type{nullptr}
|
||||
{
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
open(name, mode);
|
||||
}
|
||||
|
||||
virtual ~basic_pipe_istream() = default;
|
||||
|
||||
basic_pipe_istream(const basic_pipe_istream&) = delete;
|
||||
basic_pipe_istream& operator=(const basic_pipe_istream&) = delete;
|
||||
|
||||
basic_pipe_istream(basic_pipe_istream&& other) noexcept
|
||||
:parent_type{std::move(other)}
|
||||
{
|
||||
std::swap(m_buffer, other.m_buffer);
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
}
|
||||
|
||||
basic_pipe_istream& operator=(basic_pipe_istream&& other) noexcept
|
||||
{
|
||||
parent_type::operator=(std::move(other));
|
||||
std::swap(m_buffer, other.m_buffer);
|
||||
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::in)
|
||||
{
|
||||
m_buffer->open(name, mode);
|
||||
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
|
||||
}
|
||||
|
||||
bool is_open() const noexcept
|
||||
{
|
||||
return m_buffer->is_open();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
m_buffer->close();
|
||||
}
|
||||
|
||||
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
|
||||
{
|
||||
return m_buffer.get();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class process;
|
||||
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
|
||||
|
||||
basic_pipe_istream(basic_pipe_streambuf<char_type, traits_type> buffer)
|
||||
:parent_type{nullptr}
|
||||
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
|
||||
{
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
|
||||
};
|
||||
|
||||
template<typename CharT, typename Traits = std::char_traits<CharT>>
|
||||
class basic_pipe_ostream : public std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
private:
|
||||
using parent_type = std::basic_ostream<CharT, Traits>;
|
||||
|
||||
public:
|
||||
using char_type = CharT;
|
||||
using traits_type = Traits;
|
||||
using int_type = typename Traits::int_type;
|
||||
using pos_type = typename Traits::pos_type;
|
||||
using off_type = typename Traits::off_type;
|
||||
|
||||
public:
|
||||
basic_pipe_ostream() = default;
|
||||
|
||||
explicit basic_pipe_ostream(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
|
||||
:parent_type{nullptr}
|
||||
{
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
open(name, mode);
|
||||
}
|
||||
|
||||
virtual ~basic_pipe_ostream() = default;
|
||||
|
||||
basic_pipe_ostream(const basic_pipe_ostream&) = delete;
|
||||
basic_pipe_ostream& operator=(const basic_pipe_ostream&) = delete;
|
||||
|
||||
basic_pipe_ostream(basic_pipe_ostream&& other) noexcept
|
||||
:parent_type{std::move(other)}
|
||||
{
|
||||
std::swap(m_buffer, other.m_buffer);
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
}
|
||||
|
||||
basic_pipe_ostream& operator=(basic_pipe_ostream&& other) noexcept
|
||||
{
|
||||
parent_type::operator=(std::move(other));
|
||||
std::swap(m_buffer, other.m_buffer);
|
||||
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void open(const std::string& name, std::ios_base::openmode mode = std::ios_base::out)
|
||||
{
|
||||
m_buffer->open(name, mode);
|
||||
parent_type::clear(m_buffer->is_open() ? std::ios_base::goodbit : std::ios_base::failbit);
|
||||
}
|
||||
|
||||
bool is_open() const noexcept
|
||||
{
|
||||
return m_buffer->is_open();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
m_buffer->close();
|
||||
}
|
||||
|
||||
basic_pipe_streambuf<char_type, traits_type>* rdbuf() const noexcept
|
||||
{
|
||||
return m_buffer.get();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class process;
|
||||
friend std::pair<basic_pipe_istream<char_type, traits_type>, basic_pipe_ostream<char_type, traits_type>> make_anonymous_pipe<char_type, traits_type>();
|
||||
|
||||
basic_pipe_ostream(basic_pipe_streambuf<char_type, traits_type> buffer)
|
||||
:parent_type{nullptr}
|
||||
,m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>(std::move(buffer))}
|
||||
{
|
||||
parent_type::rdbuf(m_buffer.get());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<basic_pipe_streambuf<char_type, traits_type>> m_buffer{std::make_unique<basic_pipe_streambuf<char_type, traits_type>>()};
|
||||
};
|
||||
|
||||
template<typename CharT, typename Traits>
|
||||
std::pair<basic_pipe_istream<CharT, Traits>, basic_pipe_ostream<CharT, Traits>> make_anonymous_pipe()
|
||||
{
|
||||
int fd[2];
|
||||
|
||||
if(pipe(fd))
|
||||
throw std::runtime_error{"Failed to create pipe"};
|
||||
|
||||
return std::make_pair(basic_pipe_istream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[0], std::ios_base::in}},
|
||||
basic_pipe_ostream<CharT, Traits>{basic_pipe_streambuf<CharT, Traits>{fd[1], std::ios_base::out}});
|
||||
}
|
||||
|
||||
using pipe_streambuf = basic_pipe_streambuf<char>;
|
||||
using pipe_istream = basic_pipe_istream<char>;
|
||||
using pipe_ostream = basic_pipe_ostream<char>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,849 +0,0 @@
|
|||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2021 Agadzhanov Vladimir
|
||||
Portions Copyright (c) 2021 Jerry Jacobs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef QSERIALIZER_H
|
||||
#define QSERIALIZER_H
|
||||
|
||||
/* JSON */
|
||||
#ifdef QS_HAS_JSON
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonValue>
|
||||
#endif
|
||||
|
||||
/* XML */
|
||||
#ifdef QS_HAS_XML
|
||||
#include <QtXml/QDomDocument>
|
||||
#include <QtXml/QDomElement>
|
||||
#endif
|
||||
|
||||
/* META OBJECT SYSTEM */
|
||||
#include <QVariant>
|
||||
#include <QMetaProperty>
|
||||
#include <QMetaObject>
|
||||
#include <QMetaType>
|
||||
|
||||
#include <type_traits>
|
||||
#include <QDebug>
|
||||
|
||||
#define QS_VERSION "1.2"
|
||||
|
||||
/* Generate metaObject method */
|
||||
#define QS_META_OBJECT_METHOD \
|
||||
virtual const QMetaObject * metaObject() const { \
|
||||
return &this->staticMetaObject; \
|
||||
} \
|
||||
|
||||
#define QSERIALIZABLE \
|
||||
Q_GADGET \
|
||||
QS_META_OBJECT_METHOD
|
||||
|
||||
/* Mark class as serializable */
|
||||
#define QS_SERIALIZABLE QS_META_OBJECT_METHOD
|
||||
|
||||
#ifdef QS_HAS_XML
|
||||
Q_DECLARE_METATYPE(QDomNode)
|
||||
Q_DECLARE_METATYPE(QDomElement)
|
||||
#endif
|
||||
|
||||
class QSerializer {
|
||||
Q_GADGET
|
||||
QS_SERIALIZABLE
|
||||
public:
|
||||
virtual ~QSerializer() = default;
|
||||
|
||||
#ifdef QS_HAS_JSON
|
||||
/*! \brief Convert QJsonValue in QJsonDocument as QByteArray. */
|
||||
static QByteArray toByteArray(const QJsonValue & value){
|
||||
return QJsonDocument(value.toObject()).toJson();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef QS_HAS_XML
|
||||
/*! \brief Convert QDomNode in QDomDocument as QByteArray. */
|
||||
static QByteArray toByteArray(const QDomNode & value) {
|
||||
QDomDocument doc = value.toDocument();
|
||||
return doc.toByteArray();
|
||||
}
|
||||
|
||||
/*! \brief Make xml processing instruction (hat) and returns new XML QDomDocument. On deserialization procedure all processing instructions will be ignored. */
|
||||
static QDomDocument appendXmlHat(const QDomNode &node, const QString & encoding, const QString & version = "1.0"){
|
||||
QDomDocument doc = node.toDocument();
|
||||
QDomNode xmlNode = doc.createProcessingInstruction("xml", QString("version=\"%1\" encoding=\"%2\"").arg(version).arg(encoding));
|
||||
doc.insertBefore(xmlNode, doc.firstChild());
|
||||
return doc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef QS_HAS_JSON
|
||||
/*! \brief Serialize all accessed JSON propertyes for this object. */
|
||||
QJsonObject toJson() const {
|
||||
QJsonObject json;
|
||||
for(int i = 0; i < metaObject()->propertyCount(); i++)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QJsonValue>())) {
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if(metaObject()->property(i).metaType().id() != QMetaType::QJsonValue) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
json.insert(metaObject()->property(i).name(), metaObject()->property(i).readOnGadget(this).toJsonValue());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/*! \brief Returns QByteArray representation this object using json-serialization. */
|
||||
QByteArray toRawJson() const {
|
||||
return toByteArray(toJson());
|
||||
}
|
||||
|
||||
/*! \brief Deserialize all accessed XML propertyes for this object. */
|
||||
void fromJson(const QJsonValue & val) {
|
||||
if(val.isObject())
|
||||
{
|
||||
QJsonObject json = val.toObject();
|
||||
QStringList keys = json.keys();
|
||||
int propCount = metaObject()->propertyCount();
|
||||
for(int i = 0; i < propCount; i++)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QJsonValue>())) {
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if(metaObject()->property(i).metaType().id() != QMetaType::QJsonValue) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
for(auto key : json.keys())
|
||||
{
|
||||
if(key == metaObject()->property(i).name())
|
||||
{
|
||||
metaObject()->property(i).writeOnGadget(this, json.value(key));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Deserialize all accessed JSON propertyes for this object. */
|
||||
void fromJson(const QByteArray & data) {
|
||||
fromJson(QJsonDocument::fromJson(data).object());
|
||||
}
|
||||
#endif // QS_HAS_JSON
|
||||
|
||||
#ifdef QS_HAS_XML
|
||||
/*! \brief Serialize all accessed XML propertyes for this object. */
|
||||
QDomNode toXml() const {
|
||||
QDomDocument doc;
|
||||
QDomElement el = doc.createElement(metaObject()->className());
|
||||
for(int i = 0; i < metaObject()->propertyCount(); i++)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
if(QString(metaObject()->property(i).typeName()) != QMetaType::typeName(qMetaTypeId<QDomNode>())) {
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
if(metaObject()->property(i).metaType().id() != qMetaTypeId<QDomNode>()) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
el.appendChild(QDomNode(metaObject()->property(i).readOnGadget(this).value<QDomNode>()));
|
||||
}
|
||||
doc.appendChild(el);
|
||||
return doc;
|
||||
}
|
||||
|
||||
/*! \brief Returns QByteArray representation this object using xml-serialization. */
|
||||
QByteArray toRawXml() const {
|
||||
return toByteArray(toXml());
|
||||
}
|
||||
|
||||
/*! \brief Deserialize all accessed XML propertyes for this object. */
|
||||
void fromXml(const QDomNode & val) {
|
||||
QDomNode doc = val;
|
||||
|
||||
auto n = doc.firstChildElement(metaObject()->className());
|
||||
|
||||
if(!n.isNull()) {
|
||||
for(int i = 0; i < metaObject()->propertyCount(); i++) {
|
||||
QString name = metaObject()->property(i).name();
|
||||
QDomElement tmp = metaObject()->property(i).readOnGadget(this).value<QDomNode>().firstChildElement();
|
||||
|
||||
auto f = n.firstChildElement(tmp.tagName());
|
||||
metaObject()->property(i).writeOnGadget(this, QVariant::fromValue<QDomNode>(f));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < metaObject()->propertyCount(); i++) {
|
||||
QString name = metaObject()->property(i).name();
|
||||
auto f = doc.firstChildElement(name);
|
||||
metaObject()->property(i).writeOnGadget(this, QVariant::fromValue<QDomNode>(f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Deserialize all accessed XML propertyes for this object. */
|
||||
void fromXml(const QByteArray & data) {
|
||||
QDomDocument d;
|
||||
d.setContent(data);
|
||||
fromXml(d);
|
||||
}
|
||||
#endif // QS_HAS_XML
|
||||
};
|
||||
|
||||
#define GET(prefix, name) get_##prefix##_##name
|
||||
#define SET(prefix, name) set_##prefix##_##name
|
||||
|
||||
/* Create variable */
|
||||
#define QS_DECLARE_MEMBER(type, name) \
|
||||
public : \
|
||||
type name = type(); \
|
||||
|
||||
/* Create JSON property and methods for primitive type field*/
|
||||
#ifdef QS_HAS_JSON
|
||||
#define QS_JSON_FIELD(type, name) \
|
||||
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
|
||||
private: \
|
||||
QJsonValue GET(json, name)() const { \
|
||||
QJsonValue val = QJsonValue::fromVariant(QVariant(name)); \
|
||||
return val; \
|
||||
} \
|
||||
void SET(json, name)(const QJsonValue & varname){ \
|
||||
name = varname.toVariant().value<type>(); \
|
||||
}
|
||||
#else
|
||||
#define QS_JSON_FIELD(type, name)
|
||||
#endif
|
||||
|
||||
/* Create XML property and methods for primitive type field*/
|
||||
#ifdef QS_HAS_XML
|
||||
#define QS_XML_FIELD(type, name) \
|
||||
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
|
||||
private: \
|
||||
QDomNode GET(xml, name)() const { \
|
||||
QDomDocument doc; \
|
||||
QString strname = #name; \
|
||||
QDomElement element = doc.createElement(strname); \
|
||||
QDomText valueOfProp = doc.createTextNode(QVariant(name).toString()); \
|
||||
element.appendChild(valueOfProp); \
|
||||
doc.appendChild(element); \
|
||||
return QDomNode(doc); \
|
||||
} \
|
||||
void SET(xml, name)(const QDomNode &node) { \
|
||||
if(!node.isNull() && node.isElement()){ \
|
||||
QDomElement domElement = node.toElement(); \
|
||||
if(domElement.tagName() == #name) \
|
||||
name = QVariant(domElement.text()).value<type>(); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_XML_FIELD(type, name)
|
||||
#endif
|
||||
|
||||
/* Generate JSON-property and methods for primitive type objects */
|
||||
/* This collection must be provide method append(T) (it's can be QList, QVector) */
|
||||
#ifdef QS_HAS_JSON
|
||||
#define QS_JSON_ARRAY(itemType, name) \
|
||||
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
|
||||
private: \
|
||||
QJsonValue GET(json, name)() const { \
|
||||
QJsonArray val; \
|
||||
for(int i = 0; i < name.size(); i++) \
|
||||
val.push_back(name.at(i)); \
|
||||
return QJsonValue::fromVariant(val); \
|
||||
} \
|
||||
void SET(json, name)(const QJsonValue & varname) { \
|
||||
if(!varname.isArray()) \
|
||||
return; \
|
||||
name.clear(); \
|
||||
QJsonArray val = varname.toArray(); \
|
||||
for(auto item : val) { \
|
||||
itemType tmp; \
|
||||
tmp = item.toVariant().value<itemType>(); \
|
||||
name.append(tmp); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_JSON_ARRAY(itemType, name)
|
||||
#endif
|
||||
|
||||
/* Generate XML-property and methods for primitive type objects */
|
||||
/* This collection must be provide method append(T) (it's can be QList, QVector) */
|
||||
#ifdef QS_HAS_XML
|
||||
#define QS_XML_ARRAY(itemType, name) \
|
||||
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
|
||||
private: \
|
||||
QDomNode GET(xml, name)() const { \
|
||||
QDomDocument doc; \
|
||||
QString strname = #name; \
|
||||
QDomElement arrayXml = doc.createElement(QString(strname)); \
|
||||
arrayXml.setAttribute("type", "array"); \
|
||||
\
|
||||
for(int i = 0; i < name.size(); i++) { \
|
||||
itemType item = name.at(i); \
|
||||
QDomElement itemXml = doc.createElement("item"); \
|
||||
itemXml.setAttribute("type", #itemType); \
|
||||
itemXml.setAttribute("index", i); \
|
||||
itemXml.appendChild(doc.createTextNode(QVariant(item).toString())); \
|
||||
arrayXml.appendChild(itemXml); \
|
||||
} \
|
||||
\
|
||||
doc.appendChild(arrayXml); \
|
||||
return QDomNode(doc); \
|
||||
} \
|
||||
void SET(xml, name)(const QDomNode & node) { \
|
||||
QDomNode domNode = node.firstChild(); \
|
||||
name.clear(); \
|
||||
while(!domNode.isNull()) { \
|
||||
if(domNode.isElement()) { \
|
||||
QDomElement domElement = domNode.toElement(); \
|
||||
name.append(QVariant(domElement.text()).value<itemType>()); \
|
||||
} \
|
||||
domNode = domNode.nextSibling(); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_XML_ARRAY(itemType, name)
|
||||
#endif
|
||||
|
||||
|
||||
/* Generate JSON-property and methods for some custom class */
|
||||
/* Custom type must be provide methods fromJson and toJson or inherit from QSerializer */
|
||||
#ifdef QS_HAS_JSON
|
||||
#define QS_JSON_OBJECT(type, name) \
|
||||
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
|
||||
private: \
|
||||
QJsonValue GET(json, name)() const { \
|
||||
QJsonObject val = name.toJson(); \
|
||||
return QJsonValue(val); \
|
||||
} \
|
||||
void SET(json, name)(const QJsonValue & varname) { \
|
||||
if(!varname.isObject()) \
|
||||
return; \
|
||||
name.fromJson(varname); \
|
||||
}
|
||||
#else
|
||||
#define QS_JSON_OBJECT(type, name)
|
||||
#endif
|
||||
|
||||
/* Generate XML-property and methods for some custom class */
|
||||
/* Custom type must be provide methods fromJson and toJson or inherit from QSerializer */
|
||||
#ifdef QS_HAS_XML
|
||||
#define QS_XML_OBJECT(type, name) \
|
||||
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
|
||||
private: \
|
||||
QDomNode GET(xml, name)() const { \
|
||||
return name.toXml(); \
|
||||
} \
|
||||
void SET(xml, name)(const QDomNode & node){ \
|
||||
name.fromXml(node); \
|
||||
}
|
||||
#else
|
||||
#define QS_XML_OBJECT(type, name)
|
||||
#endif
|
||||
|
||||
/* Generate JSON-property and methods for collection of custom type objects */
|
||||
/* Custom item type must be provide methods fromJson and toJson or inherit from QSerializer */
|
||||
/* This collection must be provide method append(T) (it's can be QList, QVector) */
|
||||
#ifdef QS_HAS_JSON
|
||||
#define QS_JSON_ARRAY_OBJECTS(itemType, name) \
|
||||
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json, name)) \
|
||||
private: \
|
||||
QJsonValue GET(json, name)() const { \
|
||||
QJsonArray val; \
|
||||
for(int i = 0; i < name.size(); i++) \
|
||||
val.push_back(name.at(i).toJson()); \
|
||||
return QJsonValue::fromVariant(val); \
|
||||
} \
|
||||
void SET(json, name)(const QJsonValue & varname) { \
|
||||
if(!varname.isArray()) \
|
||||
return; \
|
||||
name.clear(); \
|
||||
QJsonArray val = varname.toArray(); \
|
||||
for(int i = 0; i < val.size(); i++) { \
|
||||
itemType tmp; \
|
||||
tmp.fromJson(val.at(i)); \
|
||||
name.append(tmp); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_JSON_ARRAY_OBJECTS(itemType, name)
|
||||
#endif
|
||||
|
||||
/* Generate XML-property and methods for collection of custom type objects */
|
||||
/* Custom type must be provide methods fromXml and toXml or inherit from QSerializer */
|
||||
/* This collection must be provide method append(T) (it's can be QList, QVector) */
|
||||
#ifdef QS_HAS_XML
|
||||
#define QS_XML_ARRAY_OBJECTS(itemType, name) \
|
||||
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
|
||||
private: \
|
||||
QDomNode GET(xml, name)() const { \
|
||||
QDomDocument doc; \
|
||||
QDomElement element = doc.createElement(#name); \
|
||||
element.setAttribute("type", "array"); \
|
||||
for(int i = 0; i < name.size(); i++) \
|
||||
element.appendChild(name.at(i).toXml()); \
|
||||
doc.appendChild(element); \
|
||||
return QDomNode(doc); \
|
||||
} \
|
||||
void SET(xml, name)(const QDomNode & node) { \
|
||||
name.clear(); \
|
||||
QDomNodeList nodesList = node.childNodes(); \
|
||||
for(int i = 0; i < nodesList.size(); i++) { \
|
||||
itemType tmp; \
|
||||
tmp.fromXml(nodesList.at(i)); \
|
||||
name.append(tmp); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_XML_ARRAY_OBJECTS(itemType, name)
|
||||
#endif
|
||||
|
||||
/* Generate JSON-property and methods for dictionary of simple fields (int, bool, QString, ...) */
|
||||
/* Custom type must be inherit from QSerializer */
|
||||
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
|
||||
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, QString>, QMap<int,int>, ...*/
|
||||
#ifdef QS_HAS_JSON
|
||||
#define QS_JSON_QT_DICT(map, name) \
|
||||
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
|
||||
private: \
|
||||
QJsonValue GET(json, name)() const { \
|
||||
QJsonObject val; \
|
||||
for(auto p = name.constBegin(); p != name.constEnd(); ++p) { \
|
||||
val.insert( \
|
||||
QVariant(p.key()).toString(), \
|
||||
QJsonValue::fromVariant(QVariant(p.value()))); \
|
||||
} \
|
||||
return val; \
|
||||
} \
|
||||
void SET(json, name)(const QJsonValue & varname) { \
|
||||
QJsonObject val = varname.toObject(); \
|
||||
name.clear(); \
|
||||
for(auto p = val.constBegin() ;p != val.constEnd(); ++p) { \
|
||||
name.insert( \
|
||||
QVariant(p.key()).value<map::key_type>(), \
|
||||
QVariant(p.value()).value<map::mapped_type>()); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_JSON_QT_DICT(map, name)
|
||||
#endif
|
||||
|
||||
/* Generate XML-property and methods for dictionary of simple fields (int, bool, QString, ...) */
|
||||
/* Custom type must be inherit from QSerializer */
|
||||
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
|
||||
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, QString>, QMap<int,int>, ...*/
|
||||
#ifdef QS_HAS_XML
|
||||
#define QS_XML_QT_DICT(map, name) \
|
||||
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
|
||||
private: \
|
||||
QDomNode GET(xml, name)() const { \
|
||||
QDomDocument doc; \
|
||||
QDomElement element = doc.createElement(#name); \
|
||||
element.setAttribute("type", "map"); \
|
||||
for(auto p = name.begin(); p != name.end(); ++p) \
|
||||
{ \
|
||||
QDomElement e = doc.createElement("item"); \
|
||||
e.setAttribute("key", QVariant(p.key()).toString()); \
|
||||
e.setAttribute("value", QVariant(p.value()).toString()); \
|
||||
element.appendChild(e); \
|
||||
} \
|
||||
doc.appendChild(element); \
|
||||
return QDomNode(doc); \
|
||||
} \
|
||||
void SET(xml, name)(const QDomNode & node) { \
|
||||
if(!node.isNull() && node.isElement()) \
|
||||
{ \
|
||||
QDomElement root = node.toElement(); \
|
||||
if(root.tagName() == #name) \
|
||||
{ \
|
||||
QDomNodeList childs = root.childNodes(); \
|
||||
\
|
||||
for(int i = 0; i < childs.size(); ++i) { \
|
||||
QDomElement item = childs.at(i).toElement(); \
|
||||
name.insert(QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
|
||||
QVariant(item.attributeNode("value").value()).value<map::mapped_type>()); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_XML_QT_DICT(map, name)
|
||||
#endif
|
||||
|
||||
|
||||
/* Generate JSON-property and methods for dictionary of custom type objects */
|
||||
/* Custom type must be inherit from QSerializer */
|
||||
/* This collection must be provide method inserv(KeyT, ValueT) (it's can be QMap, QHash) */
|
||||
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, CustomSerializableType> */
|
||||
#ifdef QS_HAS_JSON
|
||||
#define QS_JSON_QT_DICT_OBJECTS(map, name) \
|
||||
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
|
||||
private: \
|
||||
QJsonValue GET(json, name)() const { \
|
||||
QJsonObject val; \
|
||||
for(auto p = name.begin(); p != name.end(); ++p) { \
|
||||
val.insert( \
|
||||
QVariant::fromValue(p.key()).toString(), \
|
||||
p.value().toJson()); \
|
||||
} \
|
||||
return val; \
|
||||
} \
|
||||
void SET(json, name)(const QJsonValue & varname) { \
|
||||
QJsonObject val = varname.toObject(); \
|
||||
name.clear(); \
|
||||
for(auto p = val.constBegin();p != val.constEnd(); ++p) { \
|
||||
map::mapped_type tmp; \
|
||||
tmp.fromJson(p.value()); \
|
||||
name.insert( \
|
||||
QVariant(p.key()).value<map::key_type>(), \
|
||||
tmp); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_JSON_QT_DICT_OBJECTS(map, name)
|
||||
#endif
|
||||
|
||||
/* Generate XML-property and methods for dictionary of custom type objects */
|
||||
/* Custom type must be inherit from QSerializer */
|
||||
/* This collection must be provide method insert(KeyT, ValueT) (it's can be QMap, QHash) */
|
||||
/* THIS IS FOR QT DICTIONARY TYPES, for example QMap<int, CustomSerializableType> */
|
||||
#ifdef QS_HAS_XML
|
||||
#define QS_XML_QT_DICT_OBJECTS(map, name) \
|
||||
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
|
||||
private: \
|
||||
QDomNode GET(xml, name)() const { \
|
||||
QDomDocument doc; \
|
||||
QDomElement element = doc.createElement(#name); \
|
||||
element.setAttribute("type", "map"); \
|
||||
for(auto p = name.begin(); p != name.end(); ++p) \
|
||||
{ \
|
||||
QDomElement e = doc.createElement("item"); \
|
||||
e.setAttribute("key", QVariant(p.key()).toString()); \
|
||||
e.appendChild(p.value().toXml()); \
|
||||
element.appendChild(e); \
|
||||
} \
|
||||
doc.appendChild(element); \
|
||||
return QDomNode(doc); \
|
||||
} \
|
||||
void SET(xml, name)(const QDomNode & node) { \
|
||||
if(!node.isNull() && node.isElement()) \
|
||||
{ \
|
||||
QDomElement root = node.toElement(); \
|
||||
if(root.tagName() == #name) \
|
||||
{ \
|
||||
QDomNodeList childs = root.childNodes(); \
|
||||
\
|
||||
for(int i = 0; i < childs.size(); ++i) { \
|
||||
QDomElement item = childs.at(i).toElement(); \
|
||||
map::mapped_type tmp; \
|
||||
tmp.fromXml(item.firstChild()); \
|
||||
name.insert(QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
|
||||
tmp); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_XML_QT_DICT_OBJECTS(map, name)
|
||||
#endif
|
||||
|
||||
/* Generate JSON-property and methods for dictionary of simple fields (int, bool, QString, ...) */
|
||||
/* Custom type must be inherit from QSerializer */
|
||||
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
|
||||
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, QString>, std::map<int,int>, ...*/
|
||||
#ifdef QS_HAS_JSON
|
||||
#define QS_JSON_STL_DICT(map, name) \
|
||||
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
|
||||
private: \
|
||||
QJsonValue GET(json, name)() const { \
|
||||
QJsonObject val; \
|
||||
for(auto p : name){ \
|
||||
val.insert( \
|
||||
QVariant::fromValue(p.first).toString(), \
|
||||
QJsonValue::fromVariant(QVariant(p.second))); \
|
||||
} \
|
||||
return val; \
|
||||
} \
|
||||
void SET(json, name)(const QJsonValue & varname) { \
|
||||
QJsonObject val = varname.toObject(); \
|
||||
name.clear(); \
|
||||
for(auto p = val.constBegin();p != val.constEnd(); ++p) { \
|
||||
name.insert(std::pair<map::key_type, map::mapped_type>( \
|
||||
QVariant(p.key()).value<map::key_type>(), \
|
||||
QVariant(p.value()).value<map::mapped_type>())); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_JSON_STL_DICT(map, name)
|
||||
#endif
|
||||
|
||||
#ifdef QS_HAS_XML
|
||||
#define QS_XML_STL_DICT(map, name) \
|
||||
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
|
||||
private: \
|
||||
QDomNode GET(xml, name)() const { \
|
||||
QDomDocument doc; \
|
||||
QDomElement element = doc.createElement(#name); \
|
||||
element.setAttribute("type", "map"); \
|
||||
for(auto p : name) \
|
||||
{ \
|
||||
QDomElement e = doc.createElement("item"); \
|
||||
e.setAttribute("key", QVariant(p.first).toString()); \
|
||||
e.setAttribute("value", QVariant(p.second).toString()); \
|
||||
element.appendChild(e); \
|
||||
} \
|
||||
doc.appendChild(element); \
|
||||
return QDomNode(doc); \
|
||||
} \
|
||||
void SET(xml, name)(const QDomNode & node) { \
|
||||
if(!node.isNull() && node.isElement()) \
|
||||
{ \
|
||||
QDomElement root = node.toElement(); \
|
||||
if(root.tagName() == #name) \
|
||||
{ \
|
||||
QDomNodeList childs = root.childNodes(); \
|
||||
\
|
||||
for(int i = 0; i < childs.size(); ++i) { \
|
||||
QDomElement item = childs.at(i).toElement(); \
|
||||
name.insert(std::pair<map::key_type, map::mapped_type>( \
|
||||
QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
|
||||
QVariant(item.attributeNode("value").value()).value<map::mapped_type>())); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_XML_STL_DICT(map, name)
|
||||
#endif
|
||||
|
||||
/* Generate JSON-property and methods for dictionary of custom type objects */
|
||||
/* Custom type must be inherit from QSerializer */
|
||||
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
|
||||
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, CustomSerializableType> */
|
||||
#ifdef QS_HAS_JSON
|
||||
#define QS_JSON_STL_DICT_OBJECTS(map, name) \
|
||||
Q_PROPERTY(QJsonValue name READ GET(json, name) WRITE SET(json,name)) \
|
||||
private: \
|
||||
QJsonValue GET(json, name)() const { \
|
||||
QJsonObject val; \
|
||||
for(auto p : name){ \
|
||||
val.insert( \
|
||||
QVariant::fromValue(p.first).toString(), \
|
||||
p.second.toJson()); \
|
||||
} \
|
||||
return val; \
|
||||
} \
|
||||
void SET(json, name)(const QJsonValue & varname) { \
|
||||
QJsonObject val = varname.toObject(); \
|
||||
name.clear(); \
|
||||
for(auto p = val.constBegin(); p != val.constEnd(); ++p) { \
|
||||
map::mapped_type tmp; \
|
||||
tmp.fromJson(p.value()); \
|
||||
name.insert(std::pair<map::key_type, map::mapped_type>( \
|
||||
QVariant(p.key()).value<map::key_type>(), \
|
||||
tmp)); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_JSON_STL_DICT_OBJECTS(map, name)
|
||||
#endif
|
||||
|
||||
/* Generate XML-property and methods for dictionary of custom type objects */
|
||||
/* Custom type must be inherit from QSerializer */
|
||||
/* This collection must be provide method insert(KeyT, ValueT) (it's can be std::map) */
|
||||
/* THIS IS FOR STL DICTIONARY TYPES, for example std::map<int, CustomSerializableType> */
|
||||
#ifdef QS_HAS_XML
|
||||
#define QS_XML_STL_DICT_OBJECTS(map, name) \
|
||||
Q_PROPERTY(QDomNode name READ GET(xml, name) WRITE SET(xml, name)) \
|
||||
private: \
|
||||
QDomNode GET(xml, name)() const { \
|
||||
QDomDocument doc; \
|
||||
QDomElement element = doc.createElement(#name); \
|
||||
element.setAttribute("type", "map"); \
|
||||
for(auto p : name) \
|
||||
{ \
|
||||
QDomElement e = doc.createElement("item"); \
|
||||
e.setAttribute("key", QVariant(p.first).toString()); \
|
||||
e.appendChild(p.second.toXml()); \
|
||||
element.appendChild(e); \
|
||||
} \
|
||||
doc.appendChild(element); \
|
||||
return QDomNode(doc); \
|
||||
} \
|
||||
void SET(xml, name)(const QDomNode & node) { \
|
||||
if(!node.isNull() && node.isElement()) \
|
||||
{ \
|
||||
QDomElement root = node.toElement(); \
|
||||
if(root.tagName() == #name) \
|
||||
{ \
|
||||
QDomNodeList childs = root.childNodes(); \
|
||||
\
|
||||
for(int i = 0; i < childs.size(); ++i) { \
|
||||
QDomElement item = childs.at(i).toElement(); \
|
||||
map::mapped_type tmp; \
|
||||
tmp.fromXml(item.firstChild()); \
|
||||
name.insert(std::pair<map::key_type, map::mapped_type> ( \
|
||||
QVariant(item.attributeNode("key").value()).value<map::key_type>(), \
|
||||
tmp)); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
#define QS_XML_STL_DICT_OBJECTS(map, name)
|
||||
#endif
|
||||
|
||||
|
||||
/* BIND: */
|
||||
/* generate serializable propertyes JSON and XML for primitive type field */
|
||||
#define QS_BIND_FIELD(type, name) \
|
||||
QS_JSON_FIELD(type, name) \
|
||||
QS_XML_FIELD(type, name) \
|
||||
|
||||
/* BIND: */
|
||||
/* generate serializable propertyes JSON and XML for collection of primitive type fields */
|
||||
#define QS_BIND_COLLECTION(itemType, name) \
|
||||
QS_JSON_ARRAY(itemType, name) \
|
||||
QS_XML_ARRAY(itemType, name) \
|
||||
|
||||
/* BIND: */
|
||||
/* generate serializable propertyes JSON and XML for custom type object */
|
||||
#define QS_BIND_OBJECT(type, name) \
|
||||
QS_JSON_OBJECT(type, name) \
|
||||
QS_XML_OBJECT(type, name) \
|
||||
|
||||
/* BIND: */
|
||||
/* generate serializable propertyes JSON and XML for collection of custom type objects */
|
||||
#define QS_BIND_COLLECTION_OBJECTS(itemType, name) \
|
||||
QS_JSON_ARRAY_OBJECTS(itemType, name) \
|
||||
QS_XML_ARRAY_OBJECTS(itemType, name) \
|
||||
|
||||
/* BIND: */
|
||||
/* generate serializable propertyes JSON and XML for dictionary with primitive value type for QT DICTIONARY TYPES */
|
||||
#define QS_BIND_QT_DICT(map, name) \
|
||||
QS_JSON_QT_DICT(map, name) \
|
||||
QS_XML_QT_DICT(map, name) \
|
||||
|
||||
/* BIND: */
|
||||
/* generate serializable propertyes JSON and XML for dictionary of custom type objects for QT DICTIONARY TYPES */
|
||||
#define QS_BIND_QT_DICT_OBJECTS(map, name) \
|
||||
QS_JSON_QT_DICT_OBJECTS(map, name) \
|
||||
QS_XML_QT_DICT_OBJECTS(map,name) \
|
||||
|
||||
|
||||
/* BIND: */
|
||||
/* generate serializable propertyes JSON and XML for dictionary with primitive value type for STL DICTIONARY TYPES */
|
||||
#define QS_BIND_STL_DICT(map, name) \
|
||||
QS_JSON_STL_DICT(map, name) \
|
||||
QS_XML_STL_DICT(map, name) \
|
||||
|
||||
|
||||
/* BIND: */
|
||||
/* generate serializable propertyes JSON and XML for dictionary of custom type objects for STL DICTIONARY TYPES */
|
||||
#define QS_BIND_STL_DICT_OBJECTS(map, name) \
|
||||
QS_JSON_STL_DICT_OBJECTS(map, name) \
|
||||
QS_XML_STL_DICT_OBJECTS(map,name) \
|
||||
|
||||
|
||||
|
||||
/* CREATE AND BIND: */
|
||||
/* Make primitive field and generate serializable propertyes */
|
||||
/* For example: QS_FIELD(int, digit), QS_FIELD(bool, flag) */
|
||||
#define QS_FIELD(type, name) \
|
||||
QS_DECLARE_MEMBER(type, name) \
|
||||
QS_BIND_FIELD(type, name) \
|
||||
|
||||
/* CREATE AND BIND: */
|
||||
/* Make collection of primitive type objects [collectionType<itemType> name] and generate serializable propertyes for this collection */
|
||||
/* This collection must be provide method append(T) (it's can be QList, QVector) */
|
||||
#define QS_COLLECTION(collectionType, itemType, name) \
|
||||
QS_DECLARE_MEMBER(collectionType<itemType>, name) \
|
||||
QS_BIND_COLLECTION(itemType, name) \
|
||||
|
||||
/* CREATE AND BIND: */
|
||||
/* Make custom class object and bind serializable propertyes */
|
||||
/* This class must be inherited from QSerializer */
|
||||
#define QS_OBJECT(type,name) \
|
||||
QS_DECLARE_MEMBER(type, name) \
|
||||
QS_BIND_OBJECT(type, name) \
|
||||
|
||||
/* CREATE AND BIND: */
|
||||
/* Make collection of custom class objects [collectionType<itemType> name] and bind serializable propertyes */
|
||||
/* This collection must be provide method append(T) (it's can be QList, QVector) */
|
||||
#define QS_COLLECTION_OBJECTS(collectionType, itemType, name) \
|
||||
QS_DECLARE_MEMBER(collectionType<itemType>, name) \
|
||||
QS_BIND_COLLECTION_OBJECTS(itemType, name) \
|
||||
|
||||
|
||||
/* CREATE AND BIND: */
|
||||
/* Make dictionary collection of simple types [dictionary<key, itemType> name] and bind serializable propertyes */
|
||||
/* This collection must be QT DICTIONARY TYPE */
|
||||
#define QS_QT_DICT(map, first, second, name) \
|
||||
public: \
|
||||
typedef map<first,second> dict_##name##_t; \
|
||||
dict_##name##_t name = dict_##name##_t(); \
|
||||
QS_BIND_QT_DICT(dict_##name##_t, name) \
|
||||
|
||||
/* CREATE AND BIND: */
|
||||
/* Make dictionary collection of custom class objects [dictionary<key, itemType> name] and bind serializable propertyes */
|
||||
/* This collection must be QT DICTIONARY TYPE */
|
||||
#define QS_QT_DICT_OBJECTS(map, first, second, name) \
|
||||
public: \
|
||||
typedef map<first,second> dict_##name##_t; \
|
||||
dict_##name##_t name = dict_##name##_t(); \
|
||||
QS_BIND_QT_DICT_OBJECTS(dict_##name##_t, name) \
|
||||
|
||||
/* CREATE AND BIND: */
|
||||
/* Make dictionary collection of simple types [dictionary<key, itemType> name] and bind serializable propertyes */
|
||||
/* This collection must be STL DICTIONARY TYPE */
|
||||
#define QS_STL_DICT(map, first, second, name) \
|
||||
public: \
|
||||
typedef map<first,second> dict_##name##_t; \
|
||||
dict_##name##_t name = dict_##name##_t(); \
|
||||
QS_BIND_STL_DICT(dict_##name##_t, name) \
|
||||
|
||||
/* CREATE AND BIND: */
|
||||
/* Make dictionary collection of custom class objects [dictionary<key, itemType> name] and bind serializable propertyes */
|
||||
/* This collection must be STL DICTIONARY TYPE */
|
||||
#define QS_STL_DICT_OBJECTS(map, first, second, name) \
|
||||
public: \
|
||||
typedef map<first,second> dict_##name##_t; \
|
||||
dict_##name##_t name = dict_##name##_t(); \
|
||||
QS_BIND_STL_DICT_OBJECTS(dict_##name##_t, name) \
|
||||
|
||||
|
||||
|
||||
#endif // QSERIALIZER_H
|
11
Telegram/SourceFiles/ayu/sync/ayu_sync_controller.cpp
Normal file
11
Telegram/SourceFiles/ayu/sync/ayu_sync_controller.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
|
||||
#include "ayu_sync_controller.h"
|
||||
|
||||
namespace AyuSync {
|
||||
} // AyuSync
|
16
Telegram/SourceFiles/ayu/sync/ayu_sync_controller.h
Normal file
16
Telegram/SourceFiles/ayu/sync/ayu_sync_controller.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace AyuSync {
|
||||
|
||||
class ayu_sync_controller {
|
||||
|
||||
};
|
||||
|
||||
} // AyuSync
|
51
Telegram/SourceFiles/ayu/sync/models.h
Normal file
51
Telegram/SourceFiles/ayu/sync/models.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class SyncEvent {
|
||||
std::string type = "sync_unspecified";
|
||||
long userId = 0;
|
||||
};
|
||||
|
||||
class SyncBatch : public SyncEvent {
|
||||
std::string type = "sync_batch";
|
||||
long userId;
|
||||
|
||||
class SyncBatchArgs {
|
||||
std::vector<SyncEvent> events;
|
||||
};
|
||||
SyncBatchArgs args;
|
||||
};
|
||||
|
||||
class SyncRead : public SyncEvent {
|
||||
std::string type = "sync_read";
|
||||
long userId;
|
||||
|
||||
class SyncReadArgs {
|
||||
long dialogId;
|
||||
int untilId;
|
||||
int unread;
|
||||
};
|
||||
SyncReadArgs args;
|
||||
};
|
||||
|
||||
class SyncForce : public SyncEvent {
|
||||
std::string type = "sync_force";
|
||||
long userId;
|
||||
|
||||
class SyncForceArgs {
|
||||
int fromDate;
|
||||
};
|
||||
SyncForceArgs args;
|
||||
};
|
||||
|
||||
class SyncForceFinish : public SyncEvent {
|
||||
std::string type = "sync_force_finish";
|
||||
long userId;
|
||||
|
||||
class SyncForceFinishArgs {
|
||||
};
|
||||
SyncForceFinishArgs args;
|
||||
};
|
||||
|
43
Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.cpp
Normal file
43
Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include "ayu_pipe_wrapper.h"
|
||||
#include "ayu/libs/bit_converter.hpp"
|
||||
|
||||
template<class T>
|
||||
void ayu_pipe_wrapper::send(T obj) {
|
||||
auto s = json(obj).dump();
|
||||
auto length = s.length();
|
||||
char lengthBuff[4];
|
||||
bit_converter::i32_to_bytes(length, false, lengthBuff);
|
||||
|
||||
os.write(lengthBuff, 4);
|
||||
os.write(s.c_str(), length);
|
||||
os.flush();
|
||||
}
|
||||
|
||||
json ayu_pipe_wrapper::receive() {
|
||||
char lengthBuff[4];
|
||||
is.read(lengthBuff, 4);
|
||||
|
||||
auto length = bit_converter::bytes_to_i32(lengthBuff, false);
|
||||
|
||||
auto sb = std::stringbuf();
|
||||
char buff[4096];
|
||||
|
||||
while (length > 0) {
|
||||
auto readSize = std::min(length, (int)sizeof(buff));
|
||||
is.read(buff, readSize);
|
||||
sb.sputn(buff, readSize);
|
||||
length -= readSize;
|
||||
}
|
||||
|
||||
auto p = json::parse(sb.str());
|
||||
return p;
|
||||
}
|
24
Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.h
Normal file
24
Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ayu/libs/json.hpp"
|
||||
#include "ayu/libs/pipe.hpp"
|
||||
#include "ayu/sync/models.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
class ayu_pipe_wrapper {
|
||||
public:
|
||||
template<class T>
|
||||
void send(T obj);
|
||||
json receive();
|
||||
private:
|
||||
nes::basic_pipe_istream<char> is{"AyuSync"};
|
||||
nes::basic_pipe_ostream<char> os{"AyuSync"};
|
||||
};
|
|
@ -5,10 +5,10 @@
|
|||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
|
||||
#include "ayu/boxes/edit_edited_mark.h"
|
||||
#include "ayu/boxes/edit_deleted_mark.h"
|
||||
#include "ayu/ui/boxes/edit_edited_mark.h"
|
||||
#include "ayu/ui/boxes/edit_deleted_mark.h"
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "ayu/settings/settings_ayu.h"
|
||||
#include "settings_ayu.h"
|
||||
|
||||
#include "lang_auto.h"
|
||||
#include "mainwindow.h"
|
|
@ -38,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
// AyuGram includes
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "ayu/boxes/voice_confirmation_box.h"
|
||||
#include "ayu/ui/boxes/voice_confirmation_box.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace HistoryView::Controls {
|
||||
|
|
|
@ -69,7 +69,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
#include "ayu/utils/ayu_profile_values.h"
|
||||
#include "ayu/ui/utils/ayu_profile_values.h"
|
||||
|
||||
namespace Info {
|
||||
namespace Profile {
|
||||
|
|
|
@ -78,7 +78,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtGui/QClipboard>
|
||||
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "ayu/boxes/confirmation_box.h"
|
||||
#include "ayu/ui/boxes/confirmation_box.h"
|
||||
#include "styles/style_ayu_icons.h"
|
||||
|
||||
namespace Window {
|
||||
|
|
Loading…
Add table
Reference in a new issue