Compare commits
57 Commits
c98e503a18
...
69b3e6d823
Author | SHA1 | Date | |
---|---|---|---|
69b3e6d823 | |||
a211fa27e3 | |||
98ad43957b | |||
367ee53e07 | |||
f0d320133e | |||
42a41176e9 | |||
f9ff02fd3d | |||
aa793d8e7f | |||
ee0cbd7697 | |||
eb1b4c5d2e | |||
50ec1b5dbf | |||
7c0d7b1635 | |||
d98c26882c | |||
6dc678a5fa | |||
19e45787d0 | |||
acd6ec499a | |||
363cbe5712 | |||
974ad0f06e | |||
69d0704945 | |||
480373feee | |||
d04f4678f8 | |||
003070de15 | |||
9d1f8fb9bc | |||
be79e893e3 | |||
3c6d5b1cf0 | |||
b89f3a6779 | |||
94c1f6ebc2 | |||
3b034954a7 | |||
45e7d42a3f | |||
52819cab24 | |||
d821890bde | |||
d93abaf5b9 | |||
3139b7bbd9 | |||
f7114668f4 | |||
791829daf0 | |||
7cac05d0ff | |||
c715937f40 | |||
e0a98626ea | |||
78ba68c824 | |||
172744b665 | |||
de44577d5d | |||
b36b842bdf | |||
924a857ed3 | |||
b523696dce | |||
7d6af9d434 | |||
0662b2c63e | |||
81ee11133c | |||
f9077e353d | |||
30f4053679 | |||
048de9c040 | |||
3cf3097094 | |||
e801626232 | |||
a5093c4aa3 | |||
c311bb5c95 | |||
887705969f | |||
316871523d | |||
a3d193516c |
@ -23,8 +23,8 @@ option(TOMATO_ASAN "Build tomato with asan (gcc/clang/msvc)" OFF)
|
|||||||
if (TOMATO_ASAN)
|
if (TOMATO_ASAN)
|
||||||
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||||
if (NOT WIN32) # exclude mingw
|
if (NOT WIN32) # exclude mingw
|
||||||
link_libraries(-fsanitize=address)
|
#link_libraries(-fsanitize=address)
|
||||||
#link_libraries(-fsanitize=address,undefined)
|
link_libraries(-fsanitize=address,undefined)
|
||||||
#link_libraries(-fsanitize=undefined)
|
#link_libraries(-fsanitize=undefined)
|
||||||
message("II enabled ASAN")
|
message("II enabled ASAN")
|
||||||
else()
|
else()
|
||||||
|
33
external/CMakeLists.txt
vendored
33
external/CMakeLists.txt
vendored
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.14...3.24 FATAL_ERROR)
|
||||||
|
|
||||||
add_subdirectory(./entt)
|
add_subdirectory(./entt)
|
||||||
|
|
||||||
@ -19,3 +19,34 @@ add_subdirectory(./stb)
|
|||||||
add_subdirectory(./libwebp)
|
add_subdirectory(./libwebp)
|
||||||
add_subdirectory(./qoi)
|
add_subdirectory(./qoi)
|
||||||
|
|
||||||
|
if (NOT TARGET nlohmann_json::nlohmann_json)
|
||||||
|
FetchContent_Declare(json
|
||||||
|
URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
|
||||||
|
URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(json)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT TARGET zstd::zstd)
|
||||||
|
# TODO: try find_package() first
|
||||||
|
# TODO: try pkg-config next (will work on most distros)
|
||||||
|
|
||||||
|
set(ZSTD_BUILD_STATIC ON)
|
||||||
|
set(ZSTD_BUILD_SHARED OFF)
|
||||||
|
set(ZSTD_BUILD_PROGRAMS OFF)
|
||||||
|
set(ZSTD_BUILD_CONTRIB OFF)
|
||||||
|
set(ZSTD_BUILD_TESTS OFF)
|
||||||
|
FetchContent_Declare(zstd
|
||||||
|
URL "https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-1.5.5.tar.gz"
|
||||||
|
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
|
||||||
|
SOURCE_SUBDIR build/cmake
|
||||||
|
EXCLUDE_FROM_ALL
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(zstd)
|
||||||
|
|
||||||
|
add_library(zstd INTERFACE) # somehow zstd fkd this up
|
||||||
|
target_include_directories(zstd INTERFACE ${zstd_SOURCE_DIR}/lib/)
|
||||||
|
target_link_libraries(zstd INTERFACE libzstd_static)
|
||||||
|
add_library(zstd::zstd ALIAS zstd)
|
||||||
|
endif()
|
||||||
|
2
external/solanaceae_tox
vendored
2
external/solanaceae_tox
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 4bd7235a739dec020365d216509474f641029113
|
Subproject commit ce81ef7cf7cea2fe2091912c9eafe787cbba6100
|
2
external/solanaceae_toxcore
vendored
2
external/solanaceae_toxcore
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 49ab40a1ba97e884bf43ab5f35cfb48fc51f91de
|
Subproject commit cf3679018be3f90db0f2f1e9433a966692976421
|
@ -284,7 +284,7 @@ static void group_private_message_handler(const Tox_Event_Group_Private_Message
|
|||||||
|
|
||||||
const uint32_t groupnumber = tox_event_group_private_message_get_group_number(event);
|
const uint32_t groupnumber = tox_event_group_private_message_get_group_number(event);
|
||||||
const uint32_t peer_id = tox_event_group_private_message_get_peer_id(event);
|
const uint32_t peer_id = tox_event_group_private_message_get_peer_id(event);
|
||||||
const Tox_Message_Type type = tox_event_group_private_message_get_type(event);
|
const Tox_Message_Type type = tox_event_group_private_message_get_message_type(event);
|
||||||
const uint8_t *message = tox_event_group_private_message_get_message(event);
|
const uint8_t *message = tox_event_group_private_message_get_message(event);
|
||||||
const size_t length = tox_event_group_private_message_get_message_length(event);
|
const size_t length = tox_event_group_private_message_get_message_length(event);
|
||||||
const Tox_Group_Message_Id pseudo_msg_id = tox_event_group_private_message_get_message_id(event);
|
const Tox_Group_Message_Id pseudo_msg_id = tox_event_group_private_message_get_message_id(event);
|
||||||
|
@ -686,7 +686,7 @@ int main(int argc, char** argv) {
|
|||||||
{
|
{
|
||||||
EventTypeTrivial{"uint32_t", "group_number"},
|
EventTypeTrivial{"uint32_t", "group_number"},
|
||||||
EventTypeTrivial{"uint32_t", "peer_id"},
|
EventTypeTrivial{"uint32_t", "peer_id"},
|
||||||
EventTypeByteRange{"name", "name_length", "length"}, // the latter two are ideally the same
|
EventTypeByteRange{"name", "name_length", "name_length"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -702,7 +702,7 @@ int main(int argc, char** argv) {
|
|||||||
{
|
{
|
||||||
EventTypeTrivial{"uint32_t", "group_number"},
|
EventTypeTrivial{"uint32_t", "group_number"},
|
||||||
EventTypeTrivial{"uint32_t", "peer_id"},
|
EventTypeTrivial{"uint32_t", "peer_id"},
|
||||||
EventTypeByteRange{"topic", "topic_length", "length"}, // the latter two are ideally the same
|
EventTypeByteRange{"topic", "topic_length", "topic_length"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -737,7 +737,7 @@ int main(int argc, char** argv) {
|
|||||||
"Group_Password",
|
"Group_Password",
|
||||||
{
|
{
|
||||||
EventTypeTrivial{"uint32_t", "group_number"},
|
EventTypeTrivial{"uint32_t", "group_number"},
|
||||||
EventTypeByteRange{"password", "password_length", "length"}, // the latter two are ideally the same
|
EventTypeByteRange{"password", "password_length", "password_length"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -745,8 +745,8 @@ int main(int argc, char** argv) {
|
|||||||
{
|
{
|
||||||
EventTypeTrivial{"uint32_t", "group_number"},
|
EventTypeTrivial{"uint32_t", "group_number"},
|
||||||
EventTypeTrivial{"uint32_t", "peer_id"},
|
EventTypeTrivial{"uint32_t", "peer_id"},
|
||||||
EventTypeTrivial{"Tox_Message_Type", "type"},
|
EventTypeTrivial{"Tox_Message_Type", "message_type"},
|
||||||
EventTypeByteRange{"message", "message_length", "length"}, // the latter two are ideally the same
|
EventTypeByteRange{"message", "message_length", "message_length"},
|
||||||
EventTypeTrivial{"uint32_t", "message_id"},
|
EventTypeTrivial{"uint32_t", "message_id"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -755,8 +755,8 @@ int main(int argc, char** argv) {
|
|||||||
{
|
{
|
||||||
EventTypeTrivial{"uint32_t", "group_number"},
|
EventTypeTrivial{"uint32_t", "group_number"},
|
||||||
EventTypeTrivial{"uint32_t", "peer_id"},
|
EventTypeTrivial{"uint32_t", "peer_id"},
|
||||||
EventTypeTrivial{"Tox_Message_Type", "type"},
|
EventTypeTrivial{"Tox_Message_Type", "message_type"},
|
||||||
EventTypeByteRange{"message", "message_length", "length"}, // the latter two are ideally the same
|
EventTypeByteRange{"message", "message_length", "message_length"},
|
||||||
EventTypeTrivial{"uint32_t", "message_id"},
|
EventTypeTrivial{"uint32_t", "message_id"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -765,7 +765,7 @@ int main(int argc, char** argv) {
|
|||||||
{
|
{
|
||||||
EventTypeTrivial{"uint32_t", "group_number"},
|
EventTypeTrivial{"uint32_t", "group_number"},
|
||||||
EventTypeTrivial{"uint32_t", "peer_id"},
|
EventTypeTrivial{"uint32_t", "peer_id"},
|
||||||
EventTypeByteRange{"data", "data_length", "length"}, // the latter two are ideally the same
|
EventTypeByteRange{"data", "data_length", "data_length"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -773,15 +773,15 @@ int main(int argc, char** argv) {
|
|||||||
{
|
{
|
||||||
EventTypeTrivial{"uint32_t", "group_number"},
|
EventTypeTrivial{"uint32_t", "group_number"},
|
||||||
EventTypeTrivial{"uint32_t", "peer_id"},
|
EventTypeTrivial{"uint32_t", "peer_id"},
|
||||||
EventTypeByteRange{"data", "data_length", "length"}, // the latter two are ideally the same
|
EventTypeByteRange{"data", "data_length", "data_length"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Group_Invite",
|
"Group_Invite",
|
||||||
{
|
{
|
||||||
EventTypeTrivial{"uint32_t", "friend_number"},
|
EventTypeTrivial{"uint32_t", "friend_number"},
|
||||||
EventTypeByteRange{"invite_data", "invite_data_length", "length"}, // the latter two are ideally the same
|
EventTypeByteRange{"invite_data", "invite_data_length", "invite_data_length"},
|
||||||
EventTypeByteRange{"group_name", "group_name_length", "group_name_length"}, // they are :)
|
EventTypeByteRange{"group_name", "group_name_length", "group_name_length"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -797,8 +797,8 @@ int main(int argc, char** argv) {
|
|||||||
EventTypeTrivial{"uint32_t", "group_number"},
|
EventTypeTrivial{"uint32_t", "group_number"},
|
||||||
EventTypeTrivial{"uint32_t", "peer_id"},
|
EventTypeTrivial{"uint32_t", "peer_id"},
|
||||||
EventTypeTrivial{"Tox_Group_Exit_Type", "exit_type"},
|
EventTypeTrivial{"Tox_Group_Exit_Type", "exit_type"},
|
||||||
EventTypeByteRange{"name", "name_length", "name_length"}, // they are :)
|
EventTypeByteRange{"name", "name_length", "name_length"},
|
||||||
EventTypeByteRange{"part_message", "part_message_length", "part_message_length"}, // they are :)
|
EventTypeByteRange{"part_message", "part_message_length", "part_message_length"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@ sh_test(
|
|||||||
args = ["$(locations %s)" % f for f in CIMPLE_FILES] + [
|
args = ["$(locations %s)" % f for f in CIMPLE_FILES] + [
|
||||||
"-Wno-boolean-return",
|
"-Wno-boolean-return",
|
||||||
"-Wno-callback-names",
|
"-Wno-callback-names",
|
||||||
|
"-Wno-enum-from-int",
|
||||||
"+RTS",
|
"+RTS",
|
||||||
"-N4",
|
"-N4",
|
||||||
"-RTS",
|
"-RTS",
|
||||||
|
12
external/toxcore/c-toxcore/toxcore/Messenger.c
vendored
12
external/toxcore/c-toxcore/toxcore/Messenger.c
vendored
@ -775,31 +775,31 @@ int m_set_statusmessage(Messenger *m, const uint8_t *status, uint16_t length)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool userstatus_from_int(uint8_t status, Userstatus *out)
|
static bool userstatus_from_int(uint8_t status, Userstatus *out_enum)
|
||||||
{
|
{
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case USERSTATUS_NONE: {
|
case USERSTATUS_NONE: {
|
||||||
*out = USERSTATUS_NONE;
|
*out_enum = USERSTATUS_NONE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case USERSTATUS_AWAY: {
|
case USERSTATUS_AWAY: {
|
||||||
*out = USERSTATUS_AWAY;
|
*out_enum = USERSTATUS_AWAY;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case USERSTATUS_BUSY: {
|
case USERSTATUS_BUSY: {
|
||||||
*out = USERSTATUS_BUSY;
|
*out_enum = USERSTATUS_BUSY;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case USERSTATUS_INVALID: {
|
case USERSTATUS_INVALID: {
|
||||||
*out = USERSTATUS_INVALID;
|
*out_enum = USERSTATUS_INVALID;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = USERSTATUS_INVALID;
|
*out_enum = USERSTATUS_INVALID;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ static Tox_Event_Group_Custom_Packet *tox_event_group_custom_packet_alloc(void *
|
|||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
|
||||||
void tox_events_handle_group_custom_packet(
|
void tox_events_handle_group_custom_packet(
|
||||||
Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t length,
|
Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t data_length,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
Tox_Event_Group_Custom_Packet *group_custom_packet = tox_event_group_custom_packet_alloc(user_data);
|
Tox_Event_Group_Custom_Packet *group_custom_packet = tox_event_group_custom_packet_alloc(user_data);
|
||||||
@ -231,5 +231,5 @@ void tox_events_handle_group_custom_packet(
|
|||||||
|
|
||||||
tox_event_group_custom_packet_set_group_number(group_custom_packet, group_number);
|
tox_event_group_custom_packet_set_group_number(group_custom_packet, group_number);
|
||||||
tox_event_group_custom_packet_set_peer_id(group_custom_packet, peer_id);
|
tox_event_group_custom_packet_set_peer_id(group_custom_packet, peer_id);
|
||||||
tox_event_group_custom_packet_set_data(group_custom_packet, data, length);
|
tox_event_group_custom_packet_set_data(group_custom_packet, data, data_length);
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ static Tox_Event_Group_Custom_Private_Packet *tox_event_group_custom_private_pac
|
|||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
|
||||||
void tox_events_handle_group_custom_private_packet(
|
void tox_events_handle_group_custom_private_packet(
|
||||||
Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t length,
|
Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *data, size_t data_length,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet = tox_event_group_custom_private_packet_alloc(user_data);
|
Tox_Event_Group_Custom_Private_Packet *group_custom_private_packet = tox_event_group_custom_private_packet_alloc(user_data);
|
||||||
@ -231,5 +231,5 @@ void tox_events_handle_group_custom_private_packet(
|
|||||||
|
|
||||||
tox_event_group_custom_private_packet_set_group_number(group_custom_private_packet, group_number);
|
tox_event_group_custom_private_packet_set_group_number(group_custom_private_packet, group_number);
|
||||||
tox_event_group_custom_private_packet_set_peer_id(group_custom_private_packet, peer_id);
|
tox_event_group_custom_private_packet_set_peer_id(group_custom_private_packet, peer_id);
|
||||||
tox_event_group_custom_private_packet_set_data(group_custom_private_packet, data, length);
|
tox_event_group_custom_private_packet_set_data(group_custom_private_packet, data, data_length);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ static Tox_Event_Group_Invite *tox_event_group_invite_alloc(void *user_data)
|
|||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
|
||||||
void tox_events_handle_group_invite(
|
void tox_events_handle_group_invite(
|
||||||
Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t length, const uint8_t *group_name, size_t group_name_length,
|
Tox *tox, uint32_t friend_number, const uint8_t *invite_data, size_t invite_data_length, const uint8_t *group_name, size_t group_name_length,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
Tox_Event_Group_Invite *group_invite = tox_event_group_invite_alloc(user_data);
|
Tox_Event_Group_Invite *group_invite = tox_event_group_invite_alloc(user_data);
|
||||||
@ -258,6 +258,6 @@ void tox_events_handle_group_invite(
|
|||||||
}
|
}
|
||||||
|
|
||||||
tox_event_group_invite_set_friend_number(group_invite, friend_number);
|
tox_event_group_invite_set_friend_number(group_invite, friend_number);
|
||||||
tox_event_group_invite_set_invite_data(group_invite, invite_data, length);
|
tox_event_group_invite_set_invite_data(group_invite, invite_data, invite_data_length);
|
||||||
tox_event_group_invite_set_group_name(group_invite, group_name, group_name_length);
|
tox_event_group_invite_set_group_name(group_invite, group_name, group_name_length);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
struct Tox_Event_Group_Message {
|
struct Tox_Event_Group_Message {
|
||||||
uint32_t group_number;
|
uint32_t group_number;
|
||||||
uint32_t peer_id;
|
uint32_t peer_id;
|
||||||
Tox_Message_Type type;
|
Tox_Message_Type message_type;
|
||||||
uint8_t *message;
|
uint8_t *message;
|
||||||
uint32_t message_length;
|
uint32_t message_length;
|
||||||
uint32_t message_id;
|
uint32_t message_id;
|
||||||
@ -60,16 +60,16 @@ uint32_t tox_event_group_message_get_peer_id(const Tox_Event_Group_Message *grou
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static void tox_event_group_message_set_type(Tox_Event_Group_Message *group_message,
|
static void tox_event_group_message_set_message_type(Tox_Event_Group_Message *group_message,
|
||||||
Tox_Message_Type type)
|
Tox_Message_Type message_type)
|
||||||
{
|
{
|
||||||
assert(group_message != nullptr);
|
assert(group_message != nullptr);
|
||||||
group_message->type = type;
|
group_message->message_type = message_type;
|
||||||
}
|
}
|
||||||
Tox_Message_Type tox_event_group_message_get_type(const Tox_Event_Group_Message *group_message)
|
Tox_Message_Type tox_event_group_message_get_message_type(const Tox_Event_Group_Message *group_message)
|
||||||
{
|
{
|
||||||
assert(group_message != nullptr);
|
assert(group_message != nullptr);
|
||||||
return group_message->type;
|
return group_message->message_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
non_null(1) nullable(2)
|
non_null(1) nullable(2)
|
||||||
@ -143,7 +143,7 @@ bool tox_event_group_message_pack(
|
|||||||
return bin_pack_array(bp, 5)
|
return bin_pack_array(bp, 5)
|
||||||
&& bin_pack_u32(bp, event->group_number)
|
&& bin_pack_u32(bp, event->group_number)
|
||||||
&& bin_pack_u32(bp, event->peer_id)
|
&& bin_pack_u32(bp, event->peer_id)
|
||||||
&& tox_message_type_pack(event->type, bp)
|
&& tox_message_type_pack(event->message_type, bp)
|
||||||
&& bin_pack_bin(bp, event->message, event->message_length)
|
&& bin_pack_bin(bp, event->message, event->message_length)
|
||||||
&& bin_pack_u32(bp, event->message_id);
|
&& bin_pack_u32(bp, event->message_id);
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ static bool tox_event_group_message_unpack_into(
|
|||||||
|
|
||||||
return bin_unpack_u32(bu, &event->group_number)
|
return bin_unpack_u32(bu, &event->group_number)
|
||||||
&& bin_unpack_u32(bu, &event->peer_id)
|
&& bin_unpack_u32(bu, &event->peer_id)
|
||||||
&& tox_message_type_unpack(&event->type, bu)
|
&& tox_message_type_unpack(&event->message_type, bu)
|
||||||
&& bin_unpack_bin(bu, &event->message, &event->message_length)
|
&& bin_unpack_bin(bu, &event->message, &event->message_length)
|
||||||
&& bin_unpack_u32(bu, &event->message_id);
|
&& bin_unpack_u32(bu, &event->message_id);
|
||||||
}
|
}
|
||||||
@ -254,7 +254,7 @@ static Tox_Event_Group_Message *tox_event_group_message_alloc(void *user_data)
|
|||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
|
||||||
void tox_events_handle_group_message(
|
void tox_events_handle_group_message(
|
||||||
Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t message_id,
|
Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type message_type, const uint8_t *message, size_t message_length, uint32_t message_id,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
Tox_Event_Group_Message *group_message = tox_event_group_message_alloc(user_data);
|
Tox_Event_Group_Message *group_message = tox_event_group_message_alloc(user_data);
|
||||||
@ -265,7 +265,7 @@ void tox_events_handle_group_message(
|
|||||||
|
|
||||||
tox_event_group_message_set_group_number(group_message, group_number);
|
tox_event_group_message_set_group_number(group_message, group_number);
|
||||||
tox_event_group_message_set_peer_id(group_message, peer_id);
|
tox_event_group_message_set_peer_id(group_message, peer_id);
|
||||||
tox_event_group_message_set_type(group_message, type);
|
tox_event_group_message_set_message_type(group_message, message_type);
|
||||||
tox_event_group_message_set_message(group_message, message, length);
|
tox_event_group_message_set_message(group_message, message, message_length);
|
||||||
tox_event_group_message_set_message_id(group_message, message_id);
|
tox_event_group_message_set_message_id(group_message, message_id);
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ static Tox_Event_Group_Password *tox_event_group_password_alloc(void *user_data)
|
|||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
|
||||||
void tox_events_handle_group_password(
|
void tox_events_handle_group_password(
|
||||||
Tox *tox, uint32_t group_number, const uint8_t *password, size_t length,
|
Tox *tox, uint32_t group_number, const uint8_t *password, size_t password_length,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
Tox_Event_Group_Password *group_password = tox_event_group_password_alloc(user_data);
|
Tox_Event_Group_Password *group_password = tox_event_group_password_alloc(user_data);
|
||||||
@ -214,5 +214,5 @@ void tox_events_handle_group_password(
|
|||||||
}
|
}
|
||||||
|
|
||||||
tox_event_group_password_set_group_number(group_password, group_number);
|
tox_event_group_password_set_group_number(group_password, group_number);
|
||||||
tox_event_group_password_set_password(group_password, password, length);
|
tox_event_group_password_set_password(group_password, password, password_length);
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ static Tox_Event_Group_Peer_Name *tox_event_group_peer_name_alloc(void *user_dat
|
|||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
|
||||||
void tox_events_handle_group_peer_name(
|
void tox_events_handle_group_peer_name(
|
||||||
Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *name, size_t length,
|
Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *name, size_t name_length,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
Tox_Event_Group_Peer_Name *group_peer_name = tox_event_group_peer_name_alloc(user_data);
|
Tox_Event_Group_Peer_Name *group_peer_name = tox_event_group_peer_name_alloc(user_data);
|
||||||
@ -231,5 +231,5 @@ void tox_events_handle_group_peer_name(
|
|||||||
|
|
||||||
tox_event_group_peer_name_set_group_number(group_peer_name, group_number);
|
tox_event_group_peer_name_set_group_number(group_peer_name, group_number);
|
||||||
tox_event_group_peer_name_set_peer_id(group_peer_name, peer_id);
|
tox_event_group_peer_name_set_peer_id(group_peer_name, peer_id);
|
||||||
tox_event_group_peer_name_set_name(group_peer_name, name, length);
|
tox_event_group_peer_name_set_name(group_peer_name, name, name_length);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
struct Tox_Event_Group_Private_Message {
|
struct Tox_Event_Group_Private_Message {
|
||||||
uint32_t group_number;
|
uint32_t group_number;
|
||||||
uint32_t peer_id;
|
uint32_t peer_id;
|
||||||
Tox_Message_Type type;
|
Tox_Message_Type message_type;
|
||||||
uint8_t *message;
|
uint8_t *message;
|
||||||
uint32_t message_length;
|
uint32_t message_length;
|
||||||
uint32_t message_id;
|
uint32_t message_id;
|
||||||
@ -60,16 +60,16 @@ uint32_t tox_event_group_private_message_get_peer_id(const Tox_Event_Group_Priva
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static void tox_event_group_private_message_set_type(Tox_Event_Group_Private_Message *group_private_message,
|
static void tox_event_group_private_message_set_message_type(Tox_Event_Group_Private_Message *group_private_message,
|
||||||
Tox_Message_Type type)
|
Tox_Message_Type message_type)
|
||||||
{
|
{
|
||||||
assert(group_private_message != nullptr);
|
assert(group_private_message != nullptr);
|
||||||
group_private_message->type = type;
|
group_private_message->message_type = message_type;
|
||||||
}
|
}
|
||||||
Tox_Message_Type tox_event_group_private_message_get_type(const Tox_Event_Group_Private_Message *group_private_message)
|
Tox_Message_Type tox_event_group_private_message_get_message_type(const Tox_Event_Group_Private_Message *group_private_message)
|
||||||
{
|
{
|
||||||
assert(group_private_message != nullptr);
|
assert(group_private_message != nullptr);
|
||||||
return group_private_message->type;
|
return group_private_message->message_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
non_null(1) nullable(2)
|
non_null(1) nullable(2)
|
||||||
@ -143,7 +143,7 @@ bool tox_event_group_private_message_pack(
|
|||||||
return bin_pack_array(bp, 5)
|
return bin_pack_array(bp, 5)
|
||||||
&& bin_pack_u32(bp, event->group_number)
|
&& bin_pack_u32(bp, event->group_number)
|
||||||
&& bin_pack_u32(bp, event->peer_id)
|
&& bin_pack_u32(bp, event->peer_id)
|
||||||
&& tox_message_type_pack(event->type, bp)
|
&& tox_message_type_pack(event->message_type, bp)
|
||||||
&& bin_pack_bin(bp, event->message, event->message_length)
|
&& bin_pack_bin(bp, event->message, event->message_length)
|
||||||
&& bin_pack_u32(bp, event->message_id);
|
&& bin_pack_u32(bp, event->message_id);
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ static bool tox_event_group_private_message_unpack_into(
|
|||||||
|
|
||||||
return bin_unpack_u32(bu, &event->group_number)
|
return bin_unpack_u32(bu, &event->group_number)
|
||||||
&& bin_unpack_u32(bu, &event->peer_id)
|
&& bin_unpack_u32(bu, &event->peer_id)
|
||||||
&& tox_message_type_unpack(&event->type, bu)
|
&& tox_message_type_unpack(&event->message_type, bu)
|
||||||
&& bin_unpack_bin(bu, &event->message, &event->message_length)
|
&& bin_unpack_bin(bu, &event->message, &event->message_length)
|
||||||
&& bin_unpack_u32(bu, &event->message_id);
|
&& bin_unpack_u32(bu, &event->message_id);
|
||||||
}
|
}
|
||||||
@ -254,7 +254,7 @@ static Tox_Event_Group_Private_Message *tox_event_group_private_message_alloc(vo
|
|||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
|
||||||
void tox_events_handle_group_private_message(
|
void tox_events_handle_group_private_message(
|
||||||
Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type type, const uint8_t *message, size_t length, uint32_t message_id,
|
Tox *tox, uint32_t group_number, uint32_t peer_id, Tox_Message_Type message_type, const uint8_t *message, size_t message_length, uint32_t message_id,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
Tox_Event_Group_Private_Message *group_private_message = tox_event_group_private_message_alloc(user_data);
|
Tox_Event_Group_Private_Message *group_private_message = tox_event_group_private_message_alloc(user_data);
|
||||||
@ -265,7 +265,7 @@ void tox_events_handle_group_private_message(
|
|||||||
|
|
||||||
tox_event_group_private_message_set_group_number(group_private_message, group_number);
|
tox_event_group_private_message_set_group_number(group_private_message, group_number);
|
||||||
tox_event_group_private_message_set_peer_id(group_private_message, peer_id);
|
tox_event_group_private_message_set_peer_id(group_private_message, peer_id);
|
||||||
tox_event_group_private_message_set_type(group_private_message, type);
|
tox_event_group_private_message_set_message_type(group_private_message, message_type);
|
||||||
tox_event_group_private_message_set_message(group_private_message, message, length);
|
tox_event_group_private_message_set_message(group_private_message, message, message_length);
|
||||||
tox_event_group_private_message_set_message_id(group_private_message, message_id);
|
tox_event_group_private_message_set_message_id(group_private_message, message_id);
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ static Tox_Event_Group_Topic *tox_event_group_topic_alloc(void *user_data)
|
|||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
|
||||||
void tox_events_handle_group_topic(
|
void tox_events_handle_group_topic(
|
||||||
Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *topic, size_t length,
|
Tox *tox, uint32_t group_number, uint32_t peer_id, const uint8_t *topic, size_t topic_length,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
Tox_Event_Group_Topic *group_topic = tox_event_group_topic_alloc(user_data);
|
Tox_Event_Group_Topic *group_topic = tox_event_group_topic_alloc(user_data);
|
||||||
@ -231,5 +231,5 @@ void tox_events_handle_group_topic(
|
|||||||
|
|
||||||
tox_event_group_topic_set_group_number(group_topic, group_number);
|
tox_event_group_topic_set_group_number(group_topic, group_number);
|
||||||
tox_event_group_topic_set_peer_id(group_topic, peer_id);
|
tox_event_group_topic_set_peer_id(group_topic, peer_id);
|
||||||
tox_event_group_topic_set_topic(group_topic, topic, length);
|
tox_event_group_topic_set_topic(group_topic, topic, topic_length);
|
||||||
}
|
}
|
||||||
|
18
external/toxcore/c-toxcore/toxcore/group_pack.c
vendored
18
external/toxcore/c-toxcore/toxcore/group_pack.c
vendored
@ -26,46 +26,46 @@
|
|||||||
#include "network.h"
|
#include "network.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
bool group_privacy_state_from_int(uint8_t value, Group_Privacy_State *out)
|
bool group_privacy_state_from_int(uint8_t value, Group_Privacy_State *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case GI_PUBLIC: {
|
case GI_PUBLIC: {
|
||||||
*out = GI_PUBLIC;
|
*out_enum = GI_PUBLIC;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GI_PRIVATE: {
|
case GI_PRIVATE: {
|
||||||
*out = GI_PRIVATE;
|
*out_enum = GI_PRIVATE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = GI_PUBLIC;
|
*out_enum = GI_PUBLIC;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool group_voice_state_from_int(uint8_t value, Group_Voice_State *out)
|
bool group_voice_state_from_int(uint8_t value, Group_Voice_State *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case GV_ALL: {
|
case GV_ALL: {
|
||||||
*out = GV_ALL;
|
*out_enum = GV_ALL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GV_MODS: {
|
case GV_MODS: {
|
||||||
*out = GV_MODS;
|
*out_enum = GV_MODS;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GV_FOUNDER: {
|
case GV_FOUNDER: {
|
||||||
*out = GV_FOUNDER;
|
*out_enum = GV_FOUNDER;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = GV_ALL;
|
*out_enum = GV_ALL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,8 @@ non_null()
|
|||||||
bool gc_load_unpack_group(GC_Chat *chat, Bin_Unpack *bu);
|
bool gc_load_unpack_group(GC_Chat *chat, Bin_Unpack *bu);
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
bool group_privacy_state_from_int(uint8_t value, Group_Privacy_State *out);
|
bool group_privacy_state_from_int(uint8_t value, Group_Privacy_State *out_enum);
|
||||||
non_null()
|
non_null()
|
||||||
bool group_voice_state_from_int(uint8_t value, Group_Voice_State *out);
|
bool group_voice_state_from_int(uint8_t value, Group_Voice_State *out_enum);
|
||||||
|
|
||||||
#endif /* C_TOXCORE_TOXCORE_GROUP_PACK_H */
|
#endif /* C_TOXCORE_TOXCORE_GROUP_PACK_H */
|
||||||
|
8
external/toxcore/c-toxcore/toxcore/tox.c
vendored
8
external/toxcore/c-toxcore/toxcore/tox.c
vendored
@ -3932,7 +3932,7 @@ bool tox_group_get_password(const Tox *tox, uint32_t group_number, uint8_t *pass
|
|||||||
}
|
}
|
||||||
|
|
||||||
Tox_Group_Message_Id tox_group_send_message(
|
Tox_Group_Message_Id tox_group_send_message(
|
||||||
const Tox *tox, uint32_t group_number, Tox_Message_Type type, const uint8_t *message,
|
const Tox *tox, uint32_t group_number, Tox_Message_Type message_type, const uint8_t *message,
|
||||||
size_t length, Tox_Err_Group_Send_Message *error)
|
size_t length, Tox_Err_Group_Send_Message *error)
|
||||||
{
|
{
|
||||||
assert(tox != nullptr);
|
assert(tox != nullptr);
|
||||||
@ -3953,7 +3953,7 @@ Tox_Group_Message_Id tox_group_send_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t message_id = 0;
|
uint32_t message_id = 0;
|
||||||
const int ret = gc_send_message(chat, message, length, type, &message_id);
|
const int ret = gc_send_message(chat, message, length, message_type, &message_id);
|
||||||
tox_unlock(tox);
|
tox_unlock(tox);
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
@ -3995,7 +3995,7 @@ Tox_Group_Message_Id tox_group_send_message(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Tox_Group_Message_Id tox_group_send_private_message(const Tox *tox, uint32_t group_number, uint32_t peer_id,
|
Tox_Group_Message_Id tox_group_send_private_message(const Tox *tox, uint32_t group_number, uint32_t peer_id,
|
||||||
Tox_Message_Type type, const uint8_t *message, size_t length, Tox_Err_Group_Send_Private_Message *error)
|
Tox_Message_Type message_type, const uint8_t *message, size_t length, Tox_Err_Group_Send_Private_Message *error)
|
||||||
{
|
{
|
||||||
assert(tox != nullptr);
|
assert(tox != nullptr);
|
||||||
|
|
||||||
@ -4015,7 +4015,7 @@ Tox_Group_Message_Id tox_group_send_private_message(const Tox *tox, uint32_t gro
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t message_id = 0;
|
uint32_t message_id = 0;
|
||||||
const int ret = gc_send_private_message(chat, gc_peer_id_from_int(peer_id), type, message, length, &message_id);
|
const int ret = gc_send_private_message(chat, gc_peer_id_from_int(peer_id), message_type, message, length, &message_id);
|
||||||
tox_unlock(tox);
|
tox_unlock(tox);
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
|
57
external/toxcore/c-toxcore/toxcore/tox.h
vendored
57
external/toxcore/c-toxcore/toxcore/tox.h
vendored
@ -4094,11 +4094,11 @@ bool tox_group_peer_get_public_key(
|
|||||||
* @param group_number The group number of the group the name change is intended for.
|
* @param group_number The group number of the group the name change is intended for.
|
||||||
* @param peer_id The ID of the peer who has changed their name.
|
* @param peer_id The ID of the peer who has changed their name.
|
||||||
* @param name The name data.
|
* @param name The name data.
|
||||||
* @param length The length of the name.
|
* @param name_length The length of the name.
|
||||||
*/
|
*/
|
||||||
typedef void tox_group_peer_name_cb(
|
typedef void tox_group_peer_name_cb(
|
||||||
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id,
|
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id,
|
||||||
const uint8_t name[], size_t length, void *user_data);
|
const uint8_t name[], size_t name_length, void *user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the callback for the `group_peer_name` event. Pass NULL to unset.
|
* Set the callback for the `group_peer_name` event. Pass NULL to unset.
|
||||||
@ -4235,11 +4235,11 @@ bool tox_group_get_topic(
|
|||||||
* @param peer_id The ID of the peer who changed the topic. If the peer who set the topic
|
* @param peer_id The ID of the peer who changed the topic. If the peer who set the topic
|
||||||
* is not present in our peer list this value will be set to 0.
|
* is not present in our peer list this value will be set to 0.
|
||||||
* @param topic The topic data.
|
* @param topic The topic data.
|
||||||
* @param length The topic length.
|
* @param topic_length The topic length.
|
||||||
*/
|
*/
|
||||||
typedef void tox_group_topic_cb(
|
typedef void tox_group_topic_cb(
|
||||||
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id,
|
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id,
|
||||||
const uint8_t topic[], size_t length,
|
const uint8_t topic[], size_t topic_length,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4416,11 +4416,11 @@ bool tox_group_get_password(
|
|||||||
/**
|
/**
|
||||||
* @param group_number The group number of the group for which the password has changed.
|
* @param group_number The group number of the group for which the password has changed.
|
||||||
* @param password The new group password.
|
* @param password The new group password.
|
||||||
* @param length The length of the password.
|
* @param password_length The length of the password.
|
||||||
*/
|
*/
|
||||||
typedef void tox_group_password_cb(
|
typedef void tox_group_password_cb(
|
||||||
Tox *tox, Tox_Group_Number group_number,
|
Tox *tox, Tox_Group_Number group_number,
|
||||||
const uint8_t password[], size_t length,
|
const uint8_t password[], size_t password_length,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4493,7 +4493,7 @@ const char *tox_err_group_send_message_to_string(Tox_Err_Group_Send_Message valu
|
|||||||
* then reassemble the fragments. Messages may not be empty.
|
* then reassemble the fragments. Messages may not be empty.
|
||||||
*
|
*
|
||||||
* @param group_number The group number of the group the message is intended for.
|
* @param group_number The group number of the group the message is intended for.
|
||||||
* @param type Message type (normal, action, ...).
|
* @param message_type Message type (normal, action, ...).
|
||||||
* @param message A non-NULL pointer to the first element of a byte array
|
* @param message A non-NULL pointer to the first element of a byte array
|
||||||
* containing the message text.
|
* containing the message text.
|
||||||
* @param length Length of the message to be sent.
|
* @param length Length of the message to be sent.
|
||||||
@ -4502,7 +4502,7 @@ const char *tox_err_group_send_message_to_string(Tox_Err_Group_Send_Message valu
|
|||||||
* returned message ID value will be undefined.
|
* returned message ID value will be undefined.
|
||||||
*/
|
*/
|
||||||
Tox_Group_Message_Id tox_group_send_message(
|
Tox_Group_Message_Id tox_group_send_message(
|
||||||
const Tox *tox, Tox_Group_Number group_number, Tox_Message_Type type,
|
const Tox *tox, Tox_Group_Number group_number, Tox_Message_Type message_type,
|
||||||
const uint8_t message[], size_t length,
|
const uint8_t message[], size_t length,
|
||||||
Tox_Err_Group_Send_Message *error);
|
Tox_Err_Group_Send_Message *error);
|
||||||
|
|
||||||
@ -4533,6 +4533,11 @@ typedef enum Tox_Err_Group_Send_Private_Message {
|
|||||||
*/
|
*/
|
||||||
TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY,
|
TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The message type is invalid.
|
||||||
|
*/
|
||||||
|
TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The caller does not have the required permissions to send group messages.
|
* The caller does not have the required permissions to send group messages.
|
||||||
*/
|
*/
|
||||||
@ -4548,11 +4553,6 @@ typedef enum Tox_Err_Group_Send_Private_Message {
|
|||||||
*/
|
*/
|
||||||
TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_DISCONNECTED,
|
TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_DISCONNECTED,
|
||||||
|
|
||||||
/**
|
|
||||||
* The message type is invalid.
|
|
||||||
*/
|
|
||||||
TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE,
|
|
||||||
|
|
||||||
} Tox_Err_Group_Send_Private_Message;
|
} Tox_Err_Group_Send_Private_Message;
|
||||||
|
|
||||||
const char *tox_err_group_send_private_message_to_string(Tox_Err_Group_Send_Private_Message value);
|
const char *tox_err_group_send_private_message_to_string(Tox_Err_Group_Send_Private_Message value);
|
||||||
@ -4569,6 +4569,7 @@ const char *tox_err_group_send_private_message_to_string(Tox_Err_Group_Send_Priv
|
|||||||
*
|
*
|
||||||
* @param group_number The group number of the group the message is intended for.
|
* @param group_number The group number of the group the message is intended for.
|
||||||
* @param peer_id The ID of the peer the message is intended for.
|
* @param peer_id The ID of the peer the message is intended for.
|
||||||
|
* @param message_type The type of message (normal, action, ...).
|
||||||
* @param message A non-NULL pointer to the first element of a byte array
|
* @param message A non-NULL pointer to the first element of a byte array
|
||||||
* containing the message text.
|
* containing the message text.
|
||||||
* @param length Length of the message to be sent.
|
* @param length Length of the message to be sent.
|
||||||
@ -4576,7 +4577,7 @@ const char *tox_err_group_send_private_message_to_string(Tox_Err_Group_Send_Priv
|
|||||||
* @return true on success.
|
* @return true on success.
|
||||||
*/
|
*/
|
||||||
Tox_Group_Message_Id tox_group_send_private_message(
|
Tox_Group_Message_Id tox_group_send_private_message(
|
||||||
const Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id, Tox_Message_Type type,
|
const Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id, Tox_Message_Type message_type,
|
||||||
const uint8_t message[], size_t length,
|
const uint8_t message[], size_t length,
|
||||||
Tox_Err_Group_Send_Private_Message *error);
|
Tox_Err_Group_Send_Private_Message *error);
|
||||||
|
|
||||||
@ -4729,14 +4730,14 @@ bool tox_group_send_custom_private_packet(const Tox *tox, Tox_Group_Number group
|
|||||||
/**
|
/**
|
||||||
* @param group_number The group number of the group the message is intended for.
|
* @param group_number The group number of the group the message is intended for.
|
||||||
* @param peer_id The ID of the peer who sent the message.
|
* @param peer_id The ID of the peer who sent the message.
|
||||||
* @param type The type of message (normal, action, ...).
|
* @param message_type The type of message (normal, action, ...).
|
||||||
* @param message The message data.
|
* @param message The message data.
|
||||||
|
* @param message_length The length of the message.
|
||||||
* @param message_id A pseudo message id that clients can use to uniquely identify this group message.
|
* @param message_id A pseudo message id that clients can use to uniquely identify this group message.
|
||||||
* @param length The length of the message.
|
|
||||||
*/
|
*/
|
||||||
typedef void tox_group_message_cb(
|
typedef void tox_group_message_cb(
|
||||||
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id, Tox_Message_Type type,
|
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id, Tox_Message_Type message_type,
|
||||||
const uint8_t message[], size_t length, Tox_Group_Message_Id message_id, void *user_data);
|
const uint8_t message[], size_t message_length, Tox_Group_Message_Id message_id, void *user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the callback for the `group_message` event. Pass NULL to unset.
|
* Set the callback for the `group_message` event. Pass NULL to unset.
|
||||||
@ -4748,12 +4749,14 @@ void tox_callback_group_message(Tox *tox, tox_group_message_cb *callback);
|
|||||||
/**
|
/**
|
||||||
* @param group_number The group number of the group the private message is intended for.
|
* @param group_number The group number of the group the private message is intended for.
|
||||||
* @param peer_id The ID of the peer who sent the private message.
|
* @param peer_id The ID of the peer who sent the private message.
|
||||||
|
* @param message_type The type of message (normal, action, ...).
|
||||||
* @param message The message data.
|
* @param message The message data.
|
||||||
* @param length The length of the message.
|
* @param message_length The length of the message.
|
||||||
|
* @param message_id A pseudo message id that clients can use to uniquely identify this group message.
|
||||||
*/
|
*/
|
||||||
typedef void tox_group_private_message_cb(
|
typedef void tox_group_private_message_cb(
|
||||||
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id, Tox_Message_Type type,
|
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id, Tox_Message_Type message_type,
|
||||||
const uint8_t message[], size_t length, Tox_Group_Message_Id message_id, void *user_data);
|
const uint8_t message[], size_t message_length, Tox_Group_Message_Id message_id, void *user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the callback for the `group_private_message` event. Pass NULL to unset.
|
* Set the callback for the `group_private_message` event. Pass NULL to unset.
|
||||||
@ -4766,11 +4769,11 @@ void tox_callback_group_private_message(Tox *tox, tox_group_private_message_cb *
|
|||||||
* @param group_number The group number of the group the packet is intended for.
|
* @param group_number The group number of the group the packet is intended for.
|
||||||
* @param peer_id The ID of the peer who sent the packet.
|
* @param peer_id The ID of the peer who sent the packet.
|
||||||
* @param data The packet data.
|
* @param data The packet data.
|
||||||
* @param length The length of the data.
|
* @param data_length The length of the data.
|
||||||
*/
|
*/
|
||||||
typedef void tox_group_custom_packet_cb(
|
typedef void tox_group_custom_packet_cb(
|
||||||
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id,
|
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id,
|
||||||
const uint8_t data[], size_t length, void *user_data);
|
const uint8_t data[], size_t data_length, void *user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the callback for the `group_custom_packet` event. Pass NULL to unset.
|
* Set the callback for the `group_custom_packet` event. Pass NULL to unset.
|
||||||
@ -4783,11 +4786,11 @@ void tox_callback_group_custom_packet(Tox *tox, tox_group_custom_packet_cb *call
|
|||||||
* @param group_number The group number of the group the packet is intended for.
|
* @param group_number The group number of the group the packet is intended for.
|
||||||
* @param peer_id The ID of the peer who sent the packet.
|
* @param peer_id The ID of the peer who sent the packet.
|
||||||
* @param data The packet data.
|
* @param data The packet data.
|
||||||
* @param length The length of the data.
|
* @param data_length The length of the data.
|
||||||
*/
|
*/
|
||||||
typedef void tox_group_custom_private_packet_cb(
|
typedef void tox_group_custom_private_packet_cb(
|
||||||
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id,
|
Tox *tox, Tox_Group_Number group_number, Tox_Group_Peer_Number peer_id,
|
||||||
const uint8_t data[], size_t length, void *user_data);
|
const uint8_t data[], size_t data_length, void *user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the callback for the `group_custom_private_packet` event. Pass NULL to unset.
|
* Set the callback for the `group_custom_private_packet` event. Pass NULL to unset.
|
||||||
@ -4923,11 +4926,11 @@ Tox_Group_Number tox_group_invite_accept(
|
|||||||
/**
|
/**
|
||||||
* @param friend_number The friend number of the contact who sent the invite.
|
* @param friend_number The friend number of the contact who sent the invite.
|
||||||
* @param invite_data The invite data.
|
* @param invite_data The invite data.
|
||||||
* @param length The length of invite_data.
|
* @param invite_data_length The length of invite_data.
|
||||||
*/
|
*/
|
||||||
typedef void tox_group_invite_cb(
|
typedef void tox_group_invite_cb(
|
||||||
Tox *tox, Tox_Friend_Number friend_number,
|
Tox *tox, Tox_Friend_Number friend_number,
|
||||||
const uint8_t invite_data[], size_t length,
|
const uint8_t invite_data[], size_t invite_data_length,
|
||||||
const uint8_t group_name[], size_t group_name_length,
|
const uint8_t group_name[], size_t group_name_length,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
|
6
external/toxcore/c-toxcore/toxcore/tox_api.c
vendored
6
external/toxcore/c-toxcore/toxcore/tox_api.c
vendored
@ -1340,6 +1340,9 @@ const char *tox_err_group_send_private_message_to_string(Tox_Err_Group_Send_Priv
|
|||||||
case TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY:
|
case TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY:
|
||||||
return "TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY";
|
return "TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_EMPTY";
|
||||||
|
|
||||||
|
case TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE:
|
||||||
|
return "TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE";
|
||||||
|
|
||||||
case TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS:
|
case TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS:
|
||||||
return "TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS";
|
return "TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_PERMISSIONS";
|
||||||
|
|
||||||
@ -1348,9 +1351,6 @@ const char *tox_err_group_send_private_message_to_string(Tox_Err_Group_Send_Priv
|
|||||||
|
|
||||||
case TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_DISCONNECTED:
|
case TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_DISCONNECTED:
|
||||||
return "TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_DISCONNECTED";
|
return "TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_DISCONNECTED";
|
||||||
|
|
||||||
case TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE:
|
|
||||||
return "TOX_ERR_GROUP_SEND_PRIVATE_MESSAGE_BAD_TYPE";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "<invalid Tox_Err_Group_Send_Private_Message>";
|
return "<invalid Tox_Err_Group_Send_Private_Message>";
|
||||||
|
86
external/toxcore/c-toxcore/toxcore/tox_event.c
vendored
86
external/toxcore/c-toxcore/toxcore/tox_event.c
vendored
@ -725,216 +725,216 @@ bool tox_event_pack(const Tox_Event *event, Bin_Pack *bp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_event_type_from_int(uint32_t value, Tox_Event_Type *out)
|
static bool tox_event_type_from_int(uint32_t value, Tox_Event_Type *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_EVENT_SELF_CONNECTION_STATUS: {
|
case TOX_EVENT_SELF_CONNECTION_STATUS: {
|
||||||
*out = TOX_EVENT_SELF_CONNECTION_STATUS;
|
*out_enum = TOX_EVENT_SELF_CONNECTION_STATUS;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_REQUEST: {
|
case TOX_EVENT_FRIEND_REQUEST: {
|
||||||
*out = TOX_EVENT_FRIEND_REQUEST;
|
*out_enum = TOX_EVENT_FRIEND_REQUEST;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_CONNECTION_STATUS: {
|
case TOX_EVENT_FRIEND_CONNECTION_STATUS: {
|
||||||
*out = TOX_EVENT_FRIEND_CONNECTION_STATUS;
|
*out_enum = TOX_EVENT_FRIEND_CONNECTION_STATUS;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_LOSSY_PACKET: {
|
case TOX_EVENT_FRIEND_LOSSY_PACKET: {
|
||||||
*out = TOX_EVENT_FRIEND_LOSSY_PACKET;
|
*out_enum = TOX_EVENT_FRIEND_LOSSY_PACKET;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_LOSSLESS_PACKET: {
|
case TOX_EVENT_FRIEND_LOSSLESS_PACKET: {
|
||||||
*out = TOX_EVENT_FRIEND_LOSSLESS_PACKET;
|
*out_enum = TOX_EVENT_FRIEND_LOSSLESS_PACKET;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_NAME: {
|
case TOX_EVENT_FRIEND_NAME: {
|
||||||
*out = TOX_EVENT_FRIEND_NAME;
|
*out_enum = TOX_EVENT_FRIEND_NAME;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_STATUS: {
|
case TOX_EVENT_FRIEND_STATUS: {
|
||||||
*out = TOX_EVENT_FRIEND_STATUS;
|
*out_enum = TOX_EVENT_FRIEND_STATUS;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_STATUS_MESSAGE: {
|
case TOX_EVENT_FRIEND_STATUS_MESSAGE: {
|
||||||
*out = TOX_EVENT_FRIEND_STATUS_MESSAGE;
|
*out_enum = TOX_EVENT_FRIEND_STATUS_MESSAGE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_MESSAGE: {
|
case TOX_EVENT_FRIEND_MESSAGE: {
|
||||||
*out = TOX_EVENT_FRIEND_MESSAGE;
|
*out_enum = TOX_EVENT_FRIEND_MESSAGE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_READ_RECEIPT: {
|
case TOX_EVENT_FRIEND_READ_RECEIPT: {
|
||||||
*out = TOX_EVENT_FRIEND_READ_RECEIPT;
|
*out_enum = TOX_EVENT_FRIEND_READ_RECEIPT;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FRIEND_TYPING: {
|
case TOX_EVENT_FRIEND_TYPING: {
|
||||||
*out = TOX_EVENT_FRIEND_TYPING;
|
*out_enum = TOX_EVENT_FRIEND_TYPING;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FILE_CHUNK_REQUEST: {
|
case TOX_EVENT_FILE_CHUNK_REQUEST: {
|
||||||
*out = TOX_EVENT_FILE_CHUNK_REQUEST;
|
*out_enum = TOX_EVENT_FILE_CHUNK_REQUEST;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FILE_RECV: {
|
case TOX_EVENT_FILE_RECV: {
|
||||||
*out = TOX_EVENT_FILE_RECV;
|
*out_enum = TOX_EVENT_FILE_RECV;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FILE_RECV_CHUNK: {
|
case TOX_EVENT_FILE_RECV_CHUNK: {
|
||||||
*out = TOX_EVENT_FILE_RECV_CHUNK;
|
*out_enum = TOX_EVENT_FILE_RECV_CHUNK;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_FILE_RECV_CONTROL: {
|
case TOX_EVENT_FILE_RECV_CONTROL: {
|
||||||
*out = TOX_EVENT_FILE_RECV_CONTROL;
|
*out_enum = TOX_EVENT_FILE_RECV_CONTROL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_CONFERENCE_INVITE: {
|
case TOX_EVENT_CONFERENCE_INVITE: {
|
||||||
*out = TOX_EVENT_CONFERENCE_INVITE;
|
*out_enum = TOX_EVENT_CONFERENCE_INVITE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_CONFERENCE_CONNECTED: {
|
case TOX_EVENT_CONFERENCE_CONNECTED: {
|
||||||
*out = TOX_EVENT_CONFERENCE_CONNECTED;
|
*out_enum = TOX_EVENT_CONFERENCE_CONNECTED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED: {
|
case TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED: {
|
||||||
*out = TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED;
|
*out_enum = TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_CONFERENCE_PEER_NAME: {
|
case TOX_EVENT_CONFERENCE_PEER_NAME: {
|
||||||
*out = TOX_EVENT_CONFERENCE_PEER_NAME;
|
*out_enum = TOX_EVENT_CONFERENCE_PEER_NAME;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_CONFERENCE_TITLE: {
|
case TOX_EVENT_CONFERENCE_TITLE: {
|
||||||
*out = TOX_EVENT_CONFERENCE_TITLE;
|
*out_enum = TOX_EVENT_CONFERENCE_TITLE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_CONFERENCE_MESSAGE: {
|
case TOX_EVENT_CONFERENCE_MESSAGE: {
|
||||||
*out = TOX_EVENT_CONFERENCE_MESSAGE;
|
*out_enum = TOX_EVENT_CONFERENCE_MESSAGE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_PEER_NAME: {
|
case TOX_EVENT_GROUP_PEER_NAME: {
|
||||||
*out = TOX_EVENT_GROUP_PEER_NAME;
|
*out_enum = TOX_EVENT_GROUP_PEER_NAME;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_PEER_STATUS: {
|
case TOX_EVENT_GROUP_PEER_STATUS: {
|
||||||
*out = TOX_EVENT_GROUP_PEER_STATUS;
|
*out_enum = TOX_EVENT_GROUP_PEER_STATUS;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_TOPIC: {
|
case TOX_EVENT_GROUP_TOPIC: {
|
||||||
*out = TOX_EVENT_GROUP_TOPIC;
|
*out_enum = TOX_EVENT_GROUP_TOPIC;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_PRIVACY_STATE: {
|
case TOX_EVENT_GROUP_PRIVACY_STATE: {
|
||||||
*out = TOX_EVENT_GROUP_PRIVACY_STATE;
|
*out_enum = TOX_EVENT_GROUP_PRIVACY_STATE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_VOICE_STATE: {
|
case TOX_EVENT_GROUP_VOICE_STATE: {
|
||||||
*out = TOX_EVENT_GROUP_VOICE_STATE;
|
*out_enum = TOX_EVENT_GROUP_VOICE_STATE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_TOPIC_LOCK: {
|
case TOX_EVENT_GROUP_TOPIC_LOCK: {
|
||||||
*out = TOX_EVENT_GROUP_TOPIC_LOCK;
|
*out_enum = TOX_EVENT_GROUP_TOPIC_LOCK;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_PEER_LIMIT: {
|
case TOX_EVENT_GROUP_PEER_LIMIT: {
|
||||||
*out = TOX_EVENT_GROUP_PEER_LIMIT;
|
*out_enum = TOX_EVENT_GROUP_PEER_LIMIT;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_PASSWORD: {
|
case TOX_EVENT_GROUP_PASSWORD: {
|
||||||
*out = TOX_EVENT_GROUP_PASSWORD;
|
*out_enum = TOX_EVENT_GROUP_PASSWORD;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_MESSAGE: {
|
case TOX_EVENT_GROUP_MESSAGE: {
|
||||||
*out = TOX_EVENT_GROUP_MESSAGE;
|
*out_enum = TOX_EVENT_GROUP_MESSAGE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_PRIVATE_MESSAGE: {
|
case TOX_EVENT_GROUP_PRIVATE_MESSAGE: {
|
||||||
*out = TOX_EVENT_GROUP_PRIVATE_MESSAGE;
|
*out_enum = TOX_EVENT_GROUP_PRIVATE_MESSAGE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_CUSTOM_PACKET: {
|
case TOX_EVENT_GROUP_CUSTOM_PACKET: {
|
||||||
*out = TOX_EVENT_GROUP_CUSTOM_PACKET;
|
*out_enum = TOX_EVENT_GROUP_CUSTOM_PACKET;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET: {
|
case TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET: {
|
||||||
*out = TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET;
|
*out_enum = TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_INVITE: {
|
case TOX_EVENT_GROUP_INVITE: {
|
||||||
*out = TOX_EVENT_GROUP_INVITE;
|
*out_enum = TOX_EVENT_GROUP_INVITE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_PEER_JOIN: {
|
case TOX_EVENT_GROUP_PEER_JOIN: {
|
||||||
*out = TOX_EVENT_GROUP_PEER_JOIN;
|
*out_enum = TOX_EVENT_GROUP_PEER_JOIN;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_PEER_EXIT: {
|
case TOX_EVENT_GROUP_PEER_EXIT: {
|
||||||
*out = TOX_EVENT_GROUP_PEER_EXIT;
|
*out_enum = TOX_EVENT_GROUP_PEER_EXIT;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_SELF_JOIN: {
|
case TOX_EVENT_GROUP_SELF_JOIN: {
|
||||||
*out = TOX_EVENT_GROUP_SELF_JOIN;
|
*out_enum = TOX_EVENT_GROUP_SELF_JOIN;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_JOIN_FAIL: {
|
case TOX_EVENT_GROUP_JOIN_FAIL: {
|
||||||
*out = TOX_EVENT_GROUP_JOIN_FAIL;
|
*out_enum = TOX_EVENT_GROUP_JOIN_FAIL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_GROUP_MODERATION: {
|
case TOX_EVENT_GROUP_MODERATION: {
|
||||||
*out = TOX_EVENT_GROUP_MODERATION;
|
*out_enum = TOX_EVENT_GROUP_MODERATION;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_DHT_GET_NODES_RESPONSE: {
|
case TOX_EVENT_DHT_GET_NODES_RESPONSE: {
|
||||||
*out = TOX_EVENT_DHT_GET_NODES_RESPONSE;
|
*out_enum = TOX_EVENT_DHT_GET_NODES_RESPONSE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_EVENT_INVALID: {
|
case TOX_EVENT_INVALID: {
|
||||||
*out = TOX_EVENT_INVALID;
|
*out_enum = TOX_EVENT_INVALID;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_EVENT_INVALID;
|
*out_enum = TOX_EVENT_INVALID;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ uint32_t tox_event_group_message_get_group_number(
|
|||||||
const Tox_Event_Group_Message *group_message);
|
const Tox_Event_Group_Message *group_message);
|
||||||
uint32_t tox_event_group_message_get_peer_id(
|
uint32_t tox_event_group_message_get_peer_id(
|
||||||
const Tox_Event_Group_Message *group_message);
|
const Tox_Event_Group_Message *group_message);
|
||||||
Tox_Message_Type tox_event_group_message_get_type(
|
Tox_Message_Type tox_event_group_message_get_message_type(
|
||||||
const Tox_Event_Group_Message *group_message);
|
const Tox_Event_Group_Message *group_message);
|
||||||
const uint8_t *tox_event_group_message_get_message(
|
const uint8_t *tox_event_group_message_get_message(
|
||||||
const Tox_Event_Group_Message *group_message);
|
const Tox_Event_Group_Message *group_message);
|
||||||
@ -262,7 +262,7 @@ uint32_t tox_event_group_private_message_get_group_number(
|
|||||||
const Tox_Event_Group_Private_Message *group_private_message);
|
const Tox_Event_Group_Private_Message *group_private_message);
|
||||||
uint32_t tox_event_group_private_message_get_peer_id(
|
uint32_t tox_event_group_private_message_get_peer_id(
|
||||||
const Tox_Event_Group_Private_Message *group_private_message);
|
const Tox_Event_Group_Private_Message *group_private_message);
|
||||||
Tox_Message_Type tox_event_group_private_message_get_type(
|
Tox_Message_Type tox_event_group_private_message_get_message_type(
|
||||||
const Tox_Event_Group_Private_Message *group_private_message);
|
const Tox_Event_Group_Private_Message *group_private_message);
|
||||||
const uint8_t *tox_event_group_private_message_get_message(
|
const uint8_t *tox_event_group_private_message_get_message(
|
||||||
const Tox_Event_Group_Private_Message *group_private_message);
|
const Tox_Event_Group_Private_Message *group_private_message);
|
||||||
|
110
external/toxcore/c-toxcore/toxcore/tox_unpack.c
vendored
110
external/toxcore/c-toxcore/toxcore/tox_unpack.c
vendored
@ -11,21 +11,21 @@
|
|||||||
#include "tox.h"
|
#include "tox.h"
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_conference_type_from_int(uint32_t value, Tox_Conference_Type *out)
|
static bool tox_conference_type_from_int(uint32_t value, Tox_Conference_Type *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_CONFERENCE_TYPE_TEXT: {
|
case TOX_CONFERENCE_TYPE_TEXT: {
|
||||||
*out = TOX_CONFERENCE_TYPE_TEXT;
|
*out_enum = TOX_CONFERENCE_TYPE_TEXT;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_CONFERENCE_TYPE_AV: {
|
case TOX_CONFERENCE_TYPE_AV: {
|
||||||
*out = TOX_CONFERENCE_TYPE_AV;
|
*out_enum = TOX_CONFERENCE_TYPE_AV;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_CONFERENCE_TYPE_TEXT;
|
*out_enum = TOX_CONFERENCE_TYPE_TEXT;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,26 +38,26 @@ bool tox_conference_type_unpack(Tox_Conference_Type *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_connection_from_int(uint32_t value, Tox_Connection *out)
|
static bool tox_connection_from_int(uint32_t value, Tox_Connection *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_CONNECTION_NONE: {
|
case TOX_CONNECTION_NONE: {
|
||||||
*out = TOX_CONNECTION_NONE;
|
*out_enum = TOX_CONNECTION_NONE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_CONNECTION_TCP: {
|
case TOX_CONNECTION_TCP: {
|
||||||
*out = TOX_CONNECTION_TCP;
|
*out_enum = TOX_CONNECTION_TCP;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_CONNECTION_UDP: {
|
case TOX_CONNECTION_UDP: {
|
||||||
*out = TOX_CONNECTION_UDP;
|
*out_enum = TOX_CONNECTION_UDP;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_CONNECTION_NONE;
|
*out_enum = TOX_CONNECTION_NONE;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,26 +71,26 @@ bool tox_connection_unpack(Tox_Connection *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_file_control_from_int(uint32_t value, Tox_File_Control *out)
|
static bool tox_file_control_from_int(uint32_t value, Tox_File_Control *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_FILE_CONTROL_RESUME: {
|
case TOX_FILE_CONTROL_RESUME: {
|
||||||
*out = TOX_FILE_CONTROL_RESUME;
|
*out_enum = TOX_FILE_CONTROL_RESUME;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_FILE_CONTROL_PAUSE: {
|
case TOX_FILE_CONTROL_PAUSE: {
|
||||||
*out = TOX_FILE_CONTROL_PAUSE;
|
*out_enum = TOX_FILE_CONTROL_PAUSE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_FILE_CONTROL_CANCEL: {
|
case TOX_FILE_CONTROL_CANCEL: {
|
||||||
*out = TOX_FILE_CONTROL_CANCEL;
|
*out_enum = TOX_FILE_CONTROL_CANCEL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_FILE_CONTROL_RESUME;
|
*out_enum = TOX_FILE_CONTROL_RESUME;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,21 +104,21 @@ bool tox_file_control_unpack(Tox_File_Control *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_message_type_from_int(uint32_t value, Tox_Message_Type *out)
|
static bool tox_message_type_from_int(uint32_t value, Tox_Message_Type *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_MESSAGE_TYPE_NORMAL: {
|
case TOX_MESSAGE_TYPE_NORMAL: {
|
||||||
*out = TOX_MESSAGE_TYPE_NORMAL;
|
*out_enum = TOX_MESSAGE_TYPE_NORMAL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_MESSAGE_TYPE_ACTION: {
|
case TOX_MESSAGE_TYPE_ACTION: {
|
||||||
*out = TOX_MESSAGE_TYPE_ACTION;
|
*out_enum = TOX_MESSAGE_TYPE_ACTION;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_MESSAGE_TYPE_NORMAL;
|
*out_enum = TOX_MESSAGE_TYPE_NORMAL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,26 +132,26 @@ bool tox_message_type_unpack(Tox_Message_Type *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_user_status_from_int(uint32_t value, Tox_User_Status *out)
|
static bool tox_user_status_from_int(uint32_t value, Tox_User_Status *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_USER_STATUS_NONE: {
|
case TOX_USER_STATUS_NONE: {
|
||||||
*out = TOX_USER_STATUS_NONE;
|
*out_enum = TOX_USER_STATUS_NONE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_USER_STATUS_AWAY: {
|
case TOX_USER_STATUS_AWAY: {
|
||||||
*out = TOX_USER_STATUS_AWAY;
|
*out_enum = TOX_USER_STATUS_AWAY;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case TOX_USER_STATUS_BUSY: {
|
case TOX_USER_STATUS_BUSY: {
|
||||||
*out = TOX_USER_STATUS_BUSY;
|
*out_enum = TOX_USER_STATUS_BUSY;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_USER_STATUS_NONE;
|
*out_enum = TOX_USER_STATUS_NONE;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,19 +165,19 @@ bool tox_user_status_unpack(Tox_User_Status *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_group_privacy_state_from_int(uint32_t value, Tox_Group_Privacy_State *out)
|
static bool tox_group_privacy_state_from_int(uint32_t value, Tox_Group_Privacy_State *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_GROUP_PRIVACY_STATE_PUBLIC: {
|
case TOX_GROUP_PRIVACY_STATE_PUBLIC: {
|
||||||
*out = TOX_GROUP_PRIVACY_STATE_PUBLIC;
|
*out_enum = TOX_GROUP_PRIVACY_STATE_PUBLIC;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_PRIVACY_STATE_PRIVATE: {
|
case TOX_GROUP_PRIVACY_STATE_PRIVATE: {
|
||||||
*out = TOX_GROUP_PRIVACY_STATE_PRIVATE;
|
*out_enum = TOX_GROUP_PRIVACY_STATE_PRIVATE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_GROUP_PRIVACY_STATE_PUBLIC;
|
*out_enum = TOX_GROUP_PRIVACY_STATE_PUBLIC;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,23 +189,23 @@ bool tox_group_privacy_state_unpack(Tox_Group_Privacy_State *val, Bin_Unpack *bu
|
|||||||
&& tox_group_privacy_state_from_int(u32, val);
|
&& tox_group_privacy_state_from_int(u32, val);
|
||||||
}
|
}
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_group_voice_state_from_int(uint32_t value, Tox_Group_Voice_State *out)
|
static bool tox_group_voice_state_from_int(uint32_t value, Tox_Group_Voice_State *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_GROUP_VOICE_STATE_ALL: {
|
case TOX_GROUP_VOICE_STATE_ALL: {
|
||||||
*out = TOX_GROUP_VOICE_STATE_ALL;
|
*out_enum = TOX_GROUP_VOICE_STATE_ALL;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_VOICE_STATE_MODERATOR: {
|
case TOX_GROUP_VOICE_STATE_MODERATOR: {
|
||||||
*out = TOX_GROUP_VOICE_STATE_MODERATOR;
|
*out_enum = TOX_GROUP_VOICE_STATE_MODERATOR;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_VOICE_STATE_FOUNDER: {
|
case TOX_GROUP_VOICE_STATE_FOUNDER: {
|
||||||
*out = TOX_GROUP_VOICE_STATE_FOUNDER;
|
*out_enum = TOX_GROUP_VOICE_STATE_FOUNDER;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_GROUP_VOICE_STATE_ALL;
|
*out_enum = TOX_GROUP_VOICE_STATE_ALL;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,19 +218,19 @@ bool tox_group_voice_state_unpack(Tox_Group_Voice_State *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_group_topic_lock_from_int(uint32_t value, Tox_Group_Topic_Lock *out)
|
static bool tox_group_topic_lock_from_int(uint32_t value, Tox_Group_Topic_Lock *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_GROUP_TOPIC_LOCK_ENABLED: {
|
case TOX_GROUP_TOPIC_LOCK_ENABLED: {
|
||||||
*out = TOX_GROUP_TOPIC_LOCK_ENABLED;
|
*out_enum = TOX_GROUP_TOPIC_LOCK_ENABLED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_TOPIC_LOCK_DISABLED: {
|
case TOX_GROUP_TOPIC_LOCK_DISABLED: {
|
||||||
*out = TOX_GROUP_TOPIC_LOCK_DISABLED;
|
*out_enum = TOX_GROUP_TOPIC_LOCK_DISABLED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_GROUP_TOPIC_LOCK_ENABLED;
|
*out_enum = TOX_GROUP_TOPIC_LOCK_ENABLED;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,23 +243,23 @@ bool tox_group_topic_lock_unpack(Tox_Group_Topic_Lock *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_group_join_fail_from_int(uint32_t value, Tox_Group_Join_Fail *out)
|
static bool tox_group_join_fail_from_int(uint32_t value, Tox_Group_Join_Fail *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_GROUP_JOIN_FAIL_PEER_LIMIT: {
|
case TOX_GROUP_JOIN_FAIL_PEER_LIMIT: {
|
||||||
*out = TOX_GROUP_JOIN_FAIL_PEER_LIMIT;
|
*out_enum = TOX_GROUP_JOIN_FAIL_PEER_LIMIT;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD: {
|
case TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD: {
|
||||||
*out = TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD;
|
*out_enum = TOX_GROUP_JOIN_FAIL_INVALID_PASSWORD;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_JOIN_FAIL_UNKNOWN: {
|
case TOX_GROUP_JOIN_FAIL_UNKNOWN: {
|
||||||
*out = TOX_GROUP_JOIN_FAIL_UNKNOWN;
|
*out_enum = TOX_GROUP_JOIN_FAIL_UNKNOWN;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_GROUP_JOIN_FAIL_PEER_LIMIT;
|
*out_enum = TOX_GROUP_JOIN_FAIL_PEER_LIMIT;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,27 +272,27 @@ bool tox_group_join_fail_unpack(Tox_Group_Join_Fail *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_group_mod_event_from_int(uint32_t value, Tox_Group_Mod_Event *out)
|
static bool tox_group_mod_event_from_int(uint32_t value, Tox_Group_Mod_Event *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_GROUP_MOD_EVENT_KICK: {
|
case TOX_GROUP_MOD_EVENT_KICK: {
|
||||||
*out = TOX_GROUP_MOD_EVENT_KICK;
|
*out_enum = TOX_GROUP_MOD_EVENT_KICK;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_MOD_EVENT_OBSERVER: {
|
case TOX_GROUP_MOD_EVENT_OBSERVER: {
|
||||||
*out = TOX_GROUP_MOD_EVENT_OBSERVER;
|
*out_enum = TOX_GROUP_MOD_EVENT_OBSERVER;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_MOD_EVENT_USER: {
|
case TOX_GROUP_MOD_EVENT_USER: {
|
||||||
*out = TOX_GROUP_MOD_EVENT_USER;
|
*out_enum = TOX_GROUP_MOD_EVENT_USER;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_MOD_EVENT_MODERATOR: {
|
case TOX_GROUP_MOD_EVENT_MODERATOR: {
|
||||||
*out = TOX_GROUP_MOD_EVENT_MODERATOR;
|
*out_enum = TOX_GROUP_MOD_EVENT_MODERATOR;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_GROUP_MOD_EVENT_KICK;
|
*out_enum = TOX_GROUP_MOD_EVENT_KICK;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,35 +305,35 @@ bool tox_group_mod_event_unpack(Tox_Group_Mod_Event *val, Bin_Unpack *bu)
|
|||||||
}
|
}
|
||||||
|
|
||||||
non_null()
|
non_null()
|
||||||
static bool tox_group_exit_type_from_int(uint32_t value, Tox_Group_Exit_Type *out)
|
static bool tox_group_exit_type_from_int(uint32_t value, Tox_Group_Exit_Type *out_enum)
|
||||||
{
|
{
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TOX_GROUP_EXIT_TYPE_QUIT: {
|
case TOX_GROUP_EXIT_TYPE_QUIT: {
|
||||||
*out = TOX_GROUP_EXIT_TYPE_QUIT;
|
*out_enum = TOX_GROUP_EXIT_TYPE_QUIT;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_EXIT_TYPE_TIMEOUT: {
|
case TOX_GROUP_EXIT_TYPE_TIMEOUT: {
|
||||||
*out = TOX_GROUP_EXIT_TYPE_TIMEOUT;
|
*out_enum = TOX_GROUP_EXIT_TYPE_TIMEOUT;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_EXIT_TYPE_DISCONNECTED: {
|
case TOX_GROUP_EXIT_TYPE_DISCONNECTED: {
|
||||||
*out = TOX_GROUP_EXIT_TYPE_DISCONNECTED;
|
*out_enum = TOX_GROUP_EXIT_TYPE_DISCONNECTED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_EXIT_TYPE_SELF_DISCONNECTED: {
|
case TOX_GROUP_EXIT_TYPE_SELF_DISCONNECTED: {
|
||||||
*out = TOX_GROUP_EXIT_TYPE_SELF_DISCONNECTED;
|
*out_enum = TOX_GROUP_EXIT_TYPE_SELF_DISCONNECTED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_EXIT_TYPE_KICK: {
|
case TOX_GROUP_EXIT_TYPE_KICK: {
|
||||||
*out = TOX_GROUP_EXIT_TYPE_KICK;
|
*out_enum = TOX_GROUP_EXIT_TYPE_KICK;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case TOX_GROUP_EXIT_TYPE_SYNC_ERROR: {
|
case TOX_GROUP_EXIT_TYPE_SYNC_ERROR: {
|
||||||
*out = TOX_GROUP_EXIT_TYPE_SYNC_ERROR;
|
*out_enum = TOX_GROUP_EXIT_TYPE_SYNC_ERROR;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
*out = TOX_GROUP_EXIT_TYPE_QUIT;
|
*out_enum = TOX_GROUP_EXIT_TYPE_QUIT;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,16 +20,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1694553957,
|
"lastModified": 1709953752,
|
||||||
"narHash": "sha256-8o15HEax53lBJjjcr5VHMpuuT6vBcrzSNB6y2iGlPaU=",
|
"narHash": "sha256-LW84B4vM1cn7E6cDNQn2LndT9iJXI1dRE5fwbNFbQa8=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e7fe745d22df5fa282b321e577fe18d4f62e0f0b",
|
"rev": "fcaa81ed3c273237217330cf342ef1873b77c80a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "release-23.05",
|
"ref": "release-23.11",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
17
flake.nix
17
flake.nix
@ -4,7 +4,7 @@
|
|||||||
# append '.?submodules=1' to the nix commands.
|
# append '.?submodules=1' to the nix commands.
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/release-23.05";
|
nixpkgs.url = "github:NixOS/nixpkgs/release-23.11";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -12,13 +12,15 @@
|
|||||||
flake-utils.lib.eachDefaultSystem (system:
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
stdenv = (pkgs.stdenvAdapters.keepDebugInfo pkgs.stdenv);
|
||||||
in {
|
in {
|
||||||
packages.default = pkgs.stdenv.mkDerivation {
|
#packages.default = pkgs.stdenv.mkDerivation {
|
||||||
|
packages.default = stdenv.mkDerivation {
|
||||||
pname = "tomato";
|
pname = "tomato";
|
||||||
version = "0.0.0";
|
version = "0.0.0";
|
||||||
|
|
||||||
src = ./.;
|
src = ./.;
|
||||||
submodules = 1;
|
submodules = 1; # does nothing
|
||||||
|
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
cmake
|
cmake
|
||||||
@ -58,6 +60,11 @@
|
|||||||
cmakeFlags = [
|
cmakeFlags = [
|
||||||
"TOMATO_ASAN=1"
|
"TOMATO_ASAN=1"
|
||||||
"CMAKE_BUILD_TYPE=RelWithDebInfo"
|
"CMAKE_BUILD_TYPE=RelWithDebInfo"
|
||||||
|
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_JSON=${pkgs.nlohmann_json.src}" # we care less about version here
|
||||||
|
# do we really care less about the version? do we need a stable abi?
|
||||||
|
|
||||||
|
"-DFETCHCONTENT_SOURCE_DIR_ZSTD=${pkgs.zstd.src}"
|
||||||
];
|
];
|
||||||
|
|
||||||
# TODO: replace with install command
|
# TODO: replace with install command
|
||||||
@ -66,7 +73,7 @@
|
|||||||
mv bin/tomato $out/bin
|
mv bin/tomato $out/bin
|
||||||
'';
|
'';
|
||||||
|
|
||||||
dontStrip = true;
|
dontStrip = true; # does nothing
|
||||||
|
|
||||||
# copied from nixpkgs's SDL2 default.nix
|
# copied from nixpkgs's SDL2 default.nix
|
||||||
# SDL is weird in that instead of just dynamically linking with
|
# SDL is weird in that instead of just dynamically linking with
|
||||||
@ -93,6 +100,8 @@
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#packages.debug = pkgs.enableDebugging self.packages.${system}.default;
|
||||||
|
|
||||||
devShells.${system}.default = pkgs.mkShell {
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
#inputsFrom = with pkgs; [ SDL2 ];
|
#inputsFrom = with pkgs; [ SDL2 ];
|
||||||
buildInputs = [ self.packages.${system}.default ]; # this makes a prebuild tomato available in the shell, do we want this?
|
buildInputs = [ self.packages.${system}.default ]; # this makes a prebuild tomato available in the shell, do we want this?
|
||||||
|
@ -1,5 +1,60 @@
|
|||||||
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||||
|
|
||||||
|
add_library(fragment_store
|
||||||
|
./fragment_store/fragment_store_i.hpp
|
||||||
|
./fragment_store/fragment_store_i.cpp
|
||||||
|
./fragment_store/types.hpp
|
||||||
|
./fragment_store/meta_components.hpp
|
||||||
|
./fragment_store/meta_components_id.inl
|
||||||
|
./fragment_store/serializer.hpp
|
||||||
|
./fragment_store/fragment_store.hpp
|
||||||
|
./fragment_store/fragment_store.cpp
|
||||||
|
|
||||||
|
./json/message_components.hpp # TODO: move
|
||||||
|
./json/tox_message_components.hpp # TODO: move
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(fragment_store PUBLIC
|
||||||
|
nlohmann_json::nlohmann_json
|
||||||
|
EnTT::EnTT
|
||||||
|
solanaceae_util
|
||||||
|
|
||||||
|
zstd::zstd
|
||||||
|
|
||||||
|
solanaceae_tox_messages # TODO: move
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
|
||||||
|
add_library(message_fragment_store
|
||||||
|
./fragment_store/message_serializer.hpp
|
||||||
|
./fragment_store/message_serializer.cpp
|
||||||
|
./fragment_store/message_fragment_store.hpp
|
||||||
|
./fragment_store/message_fragment_store.cpp
|
||||||
|
|
||||||
|
./fragment_store/register_mfs_json_message_components.hpp
|
||||||
|
./fragment_store/register_mfs_json_message_components.cpp
|
||||||
|
./fragment_store/register_mfs_json_tox_message_components.hpp
|
||||||
|
./fragment_store/register_mfs_json_tox_message_components.cpp
|
||||||
|
)
|
||||||
|
target_compile_features(message_fragment_store PRIVATE cxx_std_20)
|
||||||
|
target_link_libraries(message_fragment_store PUBLIC
|
||||||
|
fragment_store
|
||||||
|
solanaceae_message3
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
|
||||||
|
add_executable(fragment_store_test
|
||||||
|
fragment_store/test_fragstore.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(fragment_store_test PUBLIC
|
||||||
|
fragment_store
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################
|
||||||
|
|
||||||
add_executable(tomato
|
add_executable(tomato
|
||||||
./main.cpp
|
./main.cpp
|
||||||
./icon.rc
|
./icon.rc
|
||||||
@ -82,6 +137,9 @@ target_link_libraries(tomato PUBLIC
|
|||||||
solanaceae_tox_contacts
|
solanaceae_tox_contacts
|
||||||
solanaceae_tox_messages
|
solanaceae_tox_messages
|
||||||
|
|
||||||
|
fragment_store
|
||||||
|
message_fragment_store
|
||||||
|
|
||||||
SDL3::SDL3
|
SDL3::SDL3
|
||||||
|
|
||||||
imgui
|
imgui
|
||||||
|
@ -37,6 +37,18 @@ namespace Components {
|
|||||||
|
|
||||||
} // Components
|
} // Components
|
||||||
|
|
||||||
|
namespace Context {
|
||||||
|
|
||||||
|
// TODO: move back to chat log window and keep per window instead of per contact
|
||||||
|
struct CGView {
|
||||||
|
// set to the ts of the newest rendered msg
|
||||||
|
Message3Handle begin{};
|
||||||
|
// set to the ts of the oldest rendered msg
|
||||||
|
Message3Handle end{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Context
|
||||||
|
|
||||||
static constexpr float lerp(float a, float b, float t) {
|
static constexpr float lerp(float a, float b, float t) {
|
||||||
return a + t * (b - a);
|
return a + t * (b - a);
|
||||||
}
|
}
|
||||||
@ -259,28 +271,6 @@ float ChatGui4::render(float time_delta) {
|
|||||||
|
|
||||||
auto* msg_reg_ptr = _rmm.get(*_selected_contact);
|
auto* msg_reg_ptr = _rmm.get(*_selected_contact);
|
||||||
|
|
||||||
if (msg_reg_ptr != nullptr) {
|
|
||||||
const auto& mm = *msg_reg_ptr;
|
|
||||||
//const auto& unread_storage = mm.storage<Message::Components::TagUnread>();
|
|
||||||
if (const auto* unread_storage = mm.storage<Message::Components::TagUnread>(); unread_storage != nullptr && !unread_storage->empty()) {
|
|
||||||
//assert(unread_storage->size() == 0);
|
|
||||||
//assert(unread_storage.cbegin() == unread_storage.cend());
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
std::cout << "UNREAD ";
|
|
||||||
Message3 prev_ent = entt::null;
|
|
||||||
for (const Message3 e : mm.view<Message::Components::TagUnread>()) {
|
|
||||||
std::cout << entt::to_integral(e) << " ";
|
|
||||||
if (prev_ent == e) {
|
|
||||||
assert(false && "dup");
|
|
||||||
}
|
|
||||||
prev_ent = e;
|
|
||||||
}
|
|
||||||
std::cout << "\n";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr ImGuiTableFlags table_flags =
|
constexpr ImGuiTableFlags table_flags =
|
||||||
ImGuiTableFlags_BordersInnerV |
|
ImGuiTableFlags_BordersInnerV |
|
||||||
ImGuiTableFlags_RowBg |
|
ImGuiTableFlags_RowBg |
|
||||||
@ -293,6 +283,9 @@ float ChatGui4::render(float time_delta) {
|
|||||||
ImGui::TableSetupColumn("timestamp");
|
ImGui::TableSetupColumn("timestamp");
|
||||||
ImGui::TableSetupColumn("extra_info", _show_chat_extra_info ? ImGuiTableColumnFlags_None : ImGuiTableColumnFlags_Disabled);
|
ImGui::TableSetupColumn("extra_info", _show_chat_extra_info ? ImGuiTableColumnFlags_None : ImGuiTableColumnFlags_Disabled);
|
||||||
|
|
||||||
|
Message3Handle message_view_oldest; // oldest visible message
|
||||||
|
Message3Handle message_view_newest; // last visible message
|
||||||
|
|
||||||
// very hacky, and we have variable hight entries
|
// very hacky, and we have variable hight entries
|
||||||
//ImGuiListClipper clipper;
|
//ImGuiListClipper clipper;
|
||||||
|
|
||||||
@ -381,12 +374,26 @@ float ChatGui4::render(float time_delta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// use username as visibility test
|
// use username as visibility test
|
||||||
if (ImGui::IsItemVisible() && msg_reg.all_of<Message::Components::TagUnread>(e)) {
|
if (ImGui::IsItemVisible()) {
|
||||||
// get time now
|
if (msg_reg.all_of<Message::Components::TagUnread>(e)) {
|
||||||
const uint64_t ts_now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
// get time now
|
||||||
msg_reg.emplace_or_replace<Message::Components::Read>(e, ts_now);
|
const uint64_t ts_now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
msg_reg.remove<Message::Components::TagUnread>(e);
|
msg_reg.emplace_or_replace<Message::Components::Read>(e, ts_now);
|
||||||
msg_reg.emplace_or_replace<Components::UnreadFade>(e, 1.f);
|
msg_reg.remove<Message::Components::TagUnread>(e);
|
||||||
|
msg_reg.emplace_or_replace<Components::UnreadFade>(e, 1.f);
|
||||||
|
|
||||||
|
// we remove the unread tag here
|
||||||
|
_rmm.throwEventUpdate(msg_reg, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// track view
|
||||||
|
if (!static_cast<bool>(message_view_oldest)) {
|
||||||
|
message_view_oldest = {msg_reg, e};
|
||||||
|
message_view_newest = {msg_reg, e};
|
||||||
|
} else if (static_cast<bool>(message_view_newest)) {
|
||||||
|
// update to latest
|
||||||
|
message_view_newest = {msg_reg, e};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// highlight self
|
// highlight self
|
||||||
@ -539,9 +546,90 @@ float ChatGui4::render(float time_delta) {
|
|||||||
//ImGui::TableNextRow(0, TEXT_BASE_HEIGHT);
|
//ImGui::TableNextRow(0, TEXT_BASE_HEIGHT);
|
||||||
//ImGui::TableNextRow(0, TEXT_BASE_HEIGHT);
|
//ImGui::TableNextRow(0, TEXT_BASE_HEIGHT);
|
||||||
|
|
||||||
|
{ // update view cursers
|
||||||
|
if (!msg_reg.ctx().contains<Context::CGView>()) {
|
||||||
|
msg_reg.ctx().emplace<Context::CGView>();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& cg_view = msg_reg.ctx().get<Context::CGView>();
|
||||||
|
|
||||||
|
// any message in view
|
||||||
|
if (!static_cast<bool>(message_view_oldest)) {
|
||||||
|
// no message in view, we setup a view at current time, so the next frags are loaded
|
||||||
|
if (!static_cast<bool>(cg_view.begin) || !static_cast<bool>(cg_view.end)) {
|
||||||
|
// fix invalid state
|
||||||
|
if (static_cast<bool>(cg_view.begin)) {
|
||||||
|
cg_view.begin.destroy();
|
||||||
|
_rmm.throwEventDestroy(cg_view.begin);
|
||||||
|
}
|
||||||
|
if (static_cast<bool>(cg_view.end)) {
|
||||||
|
cg_view.end.destroy();
|
||||||
|
_rmm.throwEventDestroy(cg_view.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new
|
||||||
|
cg_view.begin = {msg_reg, msg_reg.create()};
|
||||||
|
cg_view.end = {msg_reg, msg_reg.create()};
|
||||||
|
|
||||||
|
cg_view.begin.emplace_or_replace<Message::Components::ViewCurserBegin>(cg_view.end);
|
||||||
|
cg_view.end.emplace_or_replace<Message::Components::ViewCurserEnd>(cg_view.begin);
|
||||||
|
|
||||||
|
cg_view.begin.get_or_emplace<Message::Components::Timestamp>().ts = Message::getTimeMS();
|
||||||
|
cg_view.end.get_or_emplace<Message::Components::Timestamp>().ts = Message::getTimeMS();
|
||||||
|
|
||||||
|
std::cout << "CG: created view FRONT begin ts\n";
|
||||||
|
_rmm.throwEventConstruct(cg_view.begin);
|
||||||
|
std::cout << "CG: created view FRONT end ts\n";
|
||||||
|
_rmm.throwEventConstruct(cg_view.end);
|
||||||
|
} // else? we do nothing?
|
||||||
|
} else {
|
||||||
|
bool begin_created {false};
|
||||||
|
if (!static_cast<bool>(cg_view.begin)) {
|
||||||
|
cg_view.begin = {msg_reg, msg_reg.create()};
|
||||||
|
begin_created = true;
|
||||||
|
}
|
||||||
|
bool end_created {false};
|
||||||
|
if (!static_cast<bool>(cg_view.end)) {
|
||||||
|
cg_view.end = {msg_reg, msg_reg.create()};
|
||||||
|
end_created = true;
|
||||||
|
}
|
||||||
|
cg_view.begin.emplace_or_replace<Message::Components::ViewCurserBegin>(cg_view.end);
|
||||||
|
cg_view.end.emplace_or_replace<Message::Components::ViewCurserEnd>(cg_view.begin);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& old_begin_ts = cg_view.begin.get_or_emplace<Message::Components::Timestamp>().ts;
|
||||||
|
if (old_begin_ts != message_view_newest.get<Message::Components::Timestamp>().ts) {
|
||||||
|
old_begin_ts = message_view_newest.get<Message::Components::Timestamp>().ts;
|
||||||
|
if (begin_created) {
|
||||||
|
std::cout << "CG: created view begin ts with " << old_begin_ts << "\n";
|
||||||
|
_rmm.throwEventConstruct(cg_view.begin);
|
||||||
|
} else {
|
||||||
|
//std::cout << "CG: updated view begin ts to " << old_begin_ts << "\n";
|
||||||
|
_rmm.throwEventUpdate(cg_view.begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& old_end_ts = cg_view.end.get_or_emplace<Message::Components::Timestamp>().ts;
|
||||||
|
if (old_end_ts != message_view_oldest.get<Message::Components::Timestamp>().ts) {
|
||||||
|
old_end_ts = message_view_oldest.get<Message::Components::Timestamp>().ts;
|
||||||
|
if (end_created) {
|
||||||
|
std::cout << "CG: created view end ts with " << old_end_ts << "\n";
|
||||||
|
_rmm.throwEventConstruct(cg_view.end);
|
||||||
|
} else {
|
||||||
|
//std::cout << "CG: updated view end ts to " << old_end_ts << "\n";
|
||||||
|
_rmm.throwEventUpdate(cg_view.end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
|
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
|
||||||
ImGui::SetScrollHereY(1.f);
|
ImGui::SetScrollHereY(1.f);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
#include "./file_selector.hpp"
|
#include "./file_selector.hpp"
|
||||||
#include "./send_image_popup.hpp"
|
#include "./send_image_popup.hpp"
|
||||||
|
|
||||||
|
// HACK: move to public msg api?
|
||||||
|
#include "./fragment_store/message_fragment_store.hpp"
|
||||||
|
|
||||||
#include <entt/container/dense_map.hpp>
|
#include <entt/container/dense_map.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -32,6 +35,7 @@ class ChatGui4 {
|
|||||||
FileSelector _fss;
|
FileSelector _fss;
|
||||||
SendImagePopup _sip;
|
SendImagePopup _sip;
|
||||||
|
|
||||||
|
// TODO: refactor this to allow multiple open contacts
|
||||||
std::optional<Contact3> _selected_contact;
|
std::optional<Contact3> _selected_contact;
|
||||||
|
|
||||||
// TODO: per contact
|
// TODO: per contact
|
||||||
|
@ -25,41 +25,41 @@ Just keeps the Fragments in memory.
|
|||||||
|
|
||||||
# File formats
|
# File formats
|
||||||
|
|
||||||
Files can be compressed and encrypted. Since compression needs the data structure to funcion, it is applied before it is encrypted.
|
Files can be compressed and encrypted. Since compression needs the data's structure to work properly, it is applied before it is encrypted.
|
||||||
|
|
||||||
### Text Json
|
### Text Json
|
||||||
|
|
||||||
Text json only makes sense for metadata if it's neither compressed nor encrypted. (otherwise its binary on disk anyway, so why waste bytes).
|
Text json only makes sense for metadata if it's neither compressed nor encrypted. (otherwise its binary on disk anyway, so why waste bytes).
|
||||||
Since the content of data is not looked at, nothing stops you from using text json and ecrypt it, but atleast basic compression is advised.
|
Since the content of data is not looked at, nothing stops you from using text json and ecrypt it, but atleast basic compression is advised.
|
||||||
|
|
||||||
A Metadata json object has the following keys:
|
A Metadata json object can have arbitrary keys, some are predefined:
|
||||||
- `enc` (uint) Encryption type of the data, if any
|
- `FragComp::DataEncryptionType` (uint) Encryption type of the data, if any
|
||||||
- `comp` (uint) Compression type of the data, if any
|
- `FragComp::DataCompressionType` (uint) Compression type of the data, if any
|
||||||
- `metadata` (obj) the
|
|
||||||
|
|
||||||
## Binary file headers
|
## Binary file headers
|
||||||
|
|
||||||
### Split Metadata
|
### Split Metadata
|
||||||
|
|
||||||
file magic bytes `SOLMET` (6 bytes)
|
msgpack array:
|
||||||
|
|
||||||
1 byte encryption type (`0x00` is none)
|
- `[0]`: file magic string `SOLMET` (6 bytes)
|
||||||
|
- `[1]`: uint8 encryption type (`0x00` is none)
|
||||||
1 byte compression type (`0x00` is none)
|
- `[2]`: uint8 compression type (`0x00` is none, `0x01` is zstd)
|
||||||
|
- `[3]`: binary metadata (optionally compressed and encrypted)
|
||||||
...metadata here...
|
|
||||||
|
|
||||||
note that the encryption and compression are for the metadata only.
|
note that the encryption and compression are for the metadata only.
|
||||||
The metadata itself contains encryption and compression info about the data.
|
The metadata itself contains encryption and compression info about the data.
|
||||||
|
|
||||||
### Split Data
|
### Split Data
|
||||||
|
|
||||||
(none) all the data is in the metadata file.
|
All the metadata is in the metadata file. (like encryption and compression)
|
||||||
This is mostly to allow direct storage for files in the Fragment store without excessive duplication.
|
This is mostly to allow direct storage for files in the Fragment store without excessive duplication.
|
||||||
Keep in mind to not use the actual file name as the data/meta file name.
|
Keep in mind to not use the actual file name as the data/meta file name.
|
||||||
|
|
||||||
### Single fragment
|
### Single fragment
|
||||||
|
|
||||||
|
Note: this format is unused for now
|
||||||
|
|
||||||
file magic bytes `SOLFIL` (6 bytes)
|
file magic bytes `SOLFIL` (6 bytes)
|
||||||
|
|
||||||
1 byte encryption type (`0x00` is none)
|
1 byte encryption type (`0x00` is none)
|
||||||
@ -70,3 +70,7 @@ file magic bytes `SOLFIL` (6 bytes)
|
|||||||
|
|
||||||
...data here...
|
...data here...
|
||||||
|
|
||||||
|
## Compression types
|
||||||
|
|
||||||
|
- `0x00` none
|
||||||
|
- `0x01` zstd (without dict)
|
||||||
|
866
src/fragment_store/fragment_store.cpp
Normal file
866
src/fragment_store/fragment_store.cpp
Normal file
@ -0,0 +1,866 @@
|
|||||||
|
#include "./fragment_store.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/util/utils.hpp>
|
||||||
|
|
||||||
|
#include <entt/entity/handle.hpp>
|
||||||
|
#include <entt/container/dense_set.hpp>
|
||||||
|
#include <entt/core/hashed_string.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include <zstd.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static const char* metaFileTypeSuffix(MetaFileType mft) {
|
||||||
|
switch (mft) {
|
||||||
|
case MetaFileType::TEXT_JSON: return ".json";
|
||||||
|
//case MetaFileType::BINARY_ARB: return ".bin";
|
||||||
|
case MetaFileType::BINARY_MSGPACK: return ".msgpack";
|
||||||
|
}
|
||||||
|
return ""; // .unk?
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentStore::FragmentStore(void) {
|
||||||
|
{ // random namespace
|
||||||
|
const auto num0 = _rng();
|
||||||
|
const auto num1 = _rng();
|
||||||
|
const auto num2 = _rng();
|
||||||
|
const auto num3 = _rng();
|
||||||
|
|
||||||
|
_session_uuid_namespace[0+0] = (num0 >> 0) & 0xff;
|
||||||
|
_session_uuid_namespace[0+1] = (num0 >> 8) & 0xff;
|
||||||
|
_session_uuid_namespace[0+2] = (num0 >> 16) & 0xff;
|
||||||
|
_session_uuid_namespace[0+3] = (num0 >> 24) & 0xff;
|
||||||
|
|
||||||
|
_session_uuid_namespace[4+0] = (num1 >> 0) & 0xff;
|
||||||
|
_session_uuid_namespace[4+1] = (num1 >> 8) & 0xff;
|
||||||
|
_session_uuid_namespace[4+2] = (num1 >> 16) & 0xff;
|
||||||
|
_session_uuid_namespace[4+3] = (num1 >> 24) & 0xff;
|
||||||
|
|
||||||
|
_session_uuid_namespace[8+0] = (num2 >> 0) & 0xff;
|
||||||
|
_session_uuid_namespace[8+1] = (num2 >> 8) & 0xff;
|
||||||
|
_session_uuid_namespace[8+2] = (num2 >> 16) & 0xff;
|
||||||
|
_session_uuid_namespace[8+3] = (num2 >> 24) & 0xff;
|
||||||
|
|
||||||
|
_session_uuid_namespace[12+0] = (num3 >> 0) & 0xff;
|
||||||
|
_session_uuid_namespace[12+1] = (num3 >> 8) & 0xff;
|
||||||
|
_session_uuid_namespace[12+2] = (num3 >> 16) & 0xff;
|
||||||
|
_session_uuid_namespace[12+3] = (num3 >> 24) & 0xff;
|
||||||
|
}
|
||||||
|
registerSerializers();
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentStore::FragmentStore(
|
||||||
|
std::array<uint8_t, 16> session_uuid_namespace
|
||||||
|
) : _session_uuid_namespace(std::move(session_uuid_namespace)) {
|
||||||
|
registerSerializers();
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentHandle FragmentStore::fragmentHandle(FragmentID fid) {
|
||||||
|
return {_reg, fid};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> FragmentStore::generateNewUID(std::array<uint8_t, 16>& uuid_namespace) {
|
||||||
|
std::vector<uint8_t> new_uid(uuid_namespace.cbegin(), uuid_namespace.cend());
|
||||||
|
new_uid.resize(new_uid.size() + 16);
|
||||||
|
|
||||||
|
const auto num0 = _rng();
|
||||||
|
const auto num1 = _rng();
|
||||||
|
const auto num2 = _rng();
|
||||||
|
const auto num3 = _rng();
|
||||||
|
|
||||||
|
new_uid[uuid_namespace.size()+0] = (num0 >> 0) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+1] = (num0 >> 8) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+2] = (num0 >> 16) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+3] = (num0 >> 24) & 0xff;
|
||||||
|
|
||||||
|
new_uid[uuid_namespace.size()+4+0] = (num1 >> 0) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+4+1] = (num1 >> 8) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+4+2] = (num1 >> 16) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+4+3] = (num1 >> 24) & 0xff;
|
||||||
|
|
||||||
|
new_uid[uuid_namespace.size()+8+0] = (num2 >> 0) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+8+1] = (num2 >> 8) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+8+2] = (num2 >> 16) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+8+3] = (num2 >> 24) & 0xff;
|
||||||
|
|
||||||
|
new_uid[uuid_namespace.size()+12+0] = (num3 >> 0) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+12+1] = (num3 >> 8) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+12+2] = (num3 >> 16) & 0xff;
|
||||||
|
new_uid[uuid_namespace.size()+12+3] = (num3 >> 24) & 0xff;
|
||||||
|
|
||||||
|
return new_uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> FragmentStore::generateNewUID(void) {
|
||||||
|
return generateNewUID(_session_uuid_namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentID FragmentStore::newFragmentMemoryOwned(
|
||||||
|
const std::vector<uint8_t>& id,
|
||||||
|
size_t initial_size
|
||||||
|
) {
|
||||||
|
{ // first check if id is already used
|
||||||
|
auto exising_id = getFragmentByID(id);
|
||||||
|
if (_reg.valid(exising_id)) {
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // next check if space in memory budget
|
||||||
|
const auto free_memory = _memory_budget - _memory_usage;
|
||||||
|
if (initial_size > free_memory) {
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually allocate and create
|
||||||
|
auto new_data = std::make_unique<std::vector<uint8_t>>(initial_size);
|
||||||
|
if (!static_cast<bool>(new_data)) {
|
||||||
|
// allocation failure
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
_memory_usage += initial_size;
|
||||||
|
|
||||||
|
const auto new_frag = _reg.create();
|
||||||
|
|
||||||
|
_reg.emplace<FragComp::ID>(new_frag, id);
|
||||||
|
// TODO: memory comp
|
||||||
|
_reg.emplace<std::unique_ptr<std::vector<uint8_t>>>(new_frag) = std::move(new_data);
|
||||||
|
|
||||||
|
throwEventConstruct(new_frag);
|
||||||
|
|
||||||
|
return new_frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentID FragmentStore::newFragmentFile(
|
||||||
|
std::string_view store_path,
|
||||||
|
MetaFileType mft,
|
||||||
|
const std::vector<uint8_t>& id
|
||||||
|
) {
|
||||||
|
{ // first check if id is already used
|
||||||
|
const auto exising_id = getFragmentByID(id);
|
||||||
|
if (_reg.valid(exising_id)) {
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (store_path.empty()) {
|
||||||
|
store_path = _default_store_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::create_directories(store_path);
|
||||||
|
|
||||||
|
const auto id_hex = bin2hex(id);
|
||||||
|
std::filesystem::path fragment_file_path;
|
||||||
|
|
||||||
|
if (id_hex.size() < 6) {
|
||||||
|
fragment_file_path = std::filesystem::path{store_path}/id_hex;
|
||||||
|
} else {
|
||||||
|
// use the first 2hex (1byte) as a subfolder
|
||||||
|
std::filesystem::create_directories(std::string{store_path} + id_hex.substr(0, 2));
|
||||||
|
fragment_file_path = std::filesystem::path{std::string{store_path} + id_hex.substr(0, 2)} / id_hex.substr(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::filesystem::exists(fragment_file_path)) {
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto new_frag = _reg.create();
|
||||||
|
|
||||||
|
_reg.emplace<FragComp::ID>(new_frag, id);
|
||||||
|
|
||||||
|
// file (info) comp
|
||||||
|
_reg.emplace<FragComp::Ephemeral::FilePath>(new_frag, fragment_file_path.generic_u8string());
|
||||||
|
|
||||||
|
_reg.emplace<FragComp::Ephemeral::MetaFileType>(new_frag, mft);
|
||||||
|
|
||||||
|
// meta needs to be synced to file
|
||||||
|
std::function<write_to_storage_fetch_data_cb> empty_data_cb = [](const uint8_t*, uint64_t) -> uint64_t { return 0; };
|
||||||
|
if (!syncToStorage(new_frag, empty_data_cb)) {
|
||||||
|
std::cerr << "FS error: syncToStorage failed while creating new fragment file\n";
|
||||||
|
_reg.destroy(new_frag);
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// while new metadata might be created here, making sure the file could be created is more important
|
||||||
|
throwEventConstruct(new_frag);
|
||||||
|
|
||||||
|
return new_frag;
|
||||||
|
}
|
||||||
|
FragmentID FragmentStore::newFragmentFile(
|
||||||
|
std::string_view store_path,
|
||||||
|
MetaFileType mft
|
||||||
|
) {
|
||||||
|
return newFragmentFile(store_path, mft, generateNewUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentID FragmentStore::getFragmentByID(
|
||||||
|
const std::vector<uint8_t>& id
|
||||||
|
) {
|
||||||
|
// TODO: accelerate
|
||||||
|
// maybe keep it sorted and binary search? hash table lookup?
|
||||||
|
for (const auto& [frag, id_comp] : _reg.view<FragComp::ID>().each()) {
|
||||||
|
if (id == id_comp.v) {
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentID FragmentStore::getFragmentCustomMatcher(
|
||||||
|
std::function<bool(FragmentID)>& fn
|
||||||
|
) {
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FragmentStore::syncToStorage(FragmentID fid, std::function<write_to_storage_fetch_data_cb>& data_cb) {
|
||||||
|
if (!_reg.valid(fid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_reg.all_of<FragComp::Ephemeral::FilePath>(fid)) {
|
||||||
|
// not a file fragment?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split object storage
|
||||||
|
|
||||||
|
MetaFileType meta_type = MetaFileType::TEXT_JSON; // TODO: better defaults
|
||||||
|
if (_reg.all_of<FragComp::Ephemeral::MetaFileType>(fid)) {
|
||||||
|
meta_type = _reg.get<FragComp::Ephemeral::MetaFileType>(fid).type;
|
||||||
|
}
|
||||||
|
|
||||||
|
Encryption meta_enc = Encryption::NONE; // TODO: better defaults
|
||||||
|
Compression meta_comp = Compression::NONE; // TODO: better defaults
|
||||||
|
|
||||||
|
if (meta_type != MetaFileType::TEXT_JSON) {
|
||||||
|
if (_reg.all_of<FragComp::Ephemeral::MetaEncryptionType>(fid)) {
|
||||||
|
meta_enc = _reg.get<FragComp::Ephemeral::MetaEncryptionType>(fid).enc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_reg.all_of<FragComp::Ephemeral::MetaCompressionType>(fid)) {
|
||||||
|
meta_comp = _reg.get<FragComp::Ephemeral::MetaCompressionType>(fid).comp;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we cant have encryption or compression
|
||||||
|
|
||||||
|
// TODO: warning/error?
|
||||||
|
|
||||||
|
// TODO: forcing for testing
|
||||||
|
//if (_reg.all_of<Components::Ephemeral::MetaEncryptionType>(fid)) {
|
||||||
|
_reg.emplace_or_replace<FragComp::Ephemeral::MetaEncryptionType>(fid, Encryption::NONE);
|
||||||
|
//}
|
||||||
|
//if (_reg.all_of<Components::Ephemeral::MetaCompressionType>(fid)) {
|
||||||
|
_reg.emplace_or_replace<FragComp::Ephemeral::MetaCompressionType>(fid, Compression::NONE);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path meta_tmp_path = _reg.get<FragComp::Ephemeral::FilePath>(fid).path + ".meta" + metaFileTypeSuffix(meta_type) + ".tmp";
|
||||||
|
meta_tmp_path.replace_filename("." + meta_tmp_path.filename().generic_u8string());
|
||||||
|
std::ofstream meta_file{
|
||||||
|
meta_tmp_path,
|
||||||
|
std::ios::out | std::ios::trunc | std::ios::binary // always binary, also for text
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!meta_file.is_open()) {
|
||||||
|
std::cerr << "FS error: failed to create temporary meta file\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Compression data_comp = Compression::NONE; // TODO: better defaults
|
||||||
|
if (_reg.all_of<FragComp::DataCompressionType>(fid)) {
|
||||||
|
data_comp = _reg.get<FragComp::DataCompressionType>(fid).comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path data_tmp_path = _reg.get<FragComp::Ephemeral::FilePath>(fid).path + ".tmp";
|
||||||
|
data_tmp_path.replace_filename("." + data_tmp_path.filename().generic_u8string());
|
||||||
|
std::ofstream data_file{
|
||||||
|
data_tmp_path,
|
||||||
|
std::ios::out | std::ios::trunc | std::ios::binary // always binary, also for text
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!data_file.is_open()) {
|
||||||
|
meta_file.close();
|
||||||
|
std::filesystem::remove(meta_tmp_path);
|
||||||
|
std::cerr << "FS error: failed to create temporary data file\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sharing code between binary msgpack and text json for now
|
||||||
|
nlohmann::json meta_data_j = nlohmann::json::object(); // metadata needs to be an object, null not allowed
|
||||||
|
// metadata file
|
||||||
|
|
||||||
|
for (const auto& [type_id, storage] : _reg.storage()) {
|
||||||
|
if (!storage.contains(fid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout << "storage type: type_id:" << type_id << " name:" << storage.type().name() << "\n";
|
||||||
|
|
||||||
|
// use type_id to find serializer
|
||||||
|
auto s_cb_it = _sc._serl_json.find(type_id);
|
||||||
|
if (s_cb_it == _sc._serl_json.end()) {
|
||||||
|
// could not find serializer, not saving
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// noooo, why cant numbers be keys
|
||||||
|
//if (meta_type == MetaFileType::BINARY_MSGPACK) { // msgpack uses the hash id instead
|
||||||
|
//s_cb_it->second(storage.value(fid), meta_data[storage.type().hash()]);
|
||||||
|
//} else if (meta_type == MetaFileType::TEXT_JSON) {
|
||||||
|
s_cb_it->second({_reg, fid}, meta_data_j[storage.type().name()]);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta_type == MetaFileType::BINARY_MSGPACK) { // binary metadata file
|
||||||
|
const std::vector<uint8_t> meta_data = nlohmann::json::to_msgpack(meta_data_j);
|
||||||
|
std::vector<uint8_t> meta_data_compressed; // empty if none
|
||||||
|
//std::vector<uint8_t> meta_data_encrypted; // empty if none
|
||||||
|
|
||||||
|
if (meta_comp == Compression::ZSTD) {
|
||||||
|
meta_data_compressed.resize(ZSTD_compressBound(meta_data.size()));
|
||||||
|
|
||||||
|
size_t const cSize = ZSTD_compress(meta_data_compressed.data(), meta_data_compressed.size(), meta_data.data(), meta_data.size(), 0); // 0 is default is probably 3
|
||||||
|
if (ZSTD_isError(cSize)) {
|
||||||
|
std::cerr << "FS error: compressing meta failed\n";
|
||||||
|
meta_data_compressed.clear();
|
||||||
|
meta_comp = Compression::NONE;
|
||||||
|
} else {
|
||||||
|
meta_data_compressed.resize(cSize);
|
||||||
|
}
|
||||||
|
} else if (meta_comp == Compression::NONE) {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
assert(false && "implement me");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: encryption
|
||||||
|
|
||||||
|
// the meta file is itself msgpack data
|
||||||
|
nlohmann::json meta_header_j = nlohmann::json::array();
|
||||||
|
meta_header_j.emplace_back() = "SOLMET";
|
||||||
|
meta_header_j.push_back(meta_enc);
|
||||||
|
meta_header_j.push_back(meta_comp);
|
||||||
|
|
||||||
|
if (false) { // TODO: encryption
|
||||||
|
} else if (!meta_data_compressed.empty()) {
|
||||||
|
meta_header_j.push_back(nlohmann::json::binary(meta_data_compressed));
|
||||||
|
} else {
|
||||||
|
meta_header_j.push_back(nlohmann::json::binary(meta_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto meta_header_data = nlohmann::json::to_msgpack(meta_header_j);
|
||||||
|
meta_file.write(reinterpret_cast<const char*>(meta_header_data.data()), meta_header_data.size());
|
||||||
|
} else if (meta_type == MetaFileType::TEXT_JSON) {
|
||||||
|
// cant be compressed or encrypted
|
||||||
|
meta_file << meta_data_j.dump(2, ' ', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now data
|
||||||
|
if (data_comp == Compression::NONE) {
|
||||||
|
std::array<uint8_t, 1024> buffer;
|
||||||
|
uint64_t buffer_actual_size {0};
|
||||||
|
do {
|
||||||
|
buffer_actual_size = data_cb(buffer.data(), buffer.size());
|
||||||
|
if (buffer_actual_size == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (buffer_actual_size > buffer.size()) {
|
||||||
|
// wtf
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_file.write(reinterpret_cast<const char*>(buffer.data()), buffer_actual_size);
|
||||||
|
} while (buffer_actual_size == buffer.size());
|
||||||
|
} else if (data_comp == Compression::ZSTD) {
|
||||||
|
std::vector<uint8_t> buffer(ZSTD_CStreamInSize());
|
||||||
|
std::vector<uint8_t> compressed_buffer(ZSTD_CStreamOutSize());
|
||||||
|
uint64_t buffer_actual_size {0};
|
||||||
|
|
||||||
|
std::unique_ptr<ZSTD_CCtx, decltype(&ZSTD_freeCCtx)> cctx{ZSTD_createCCtx(), &ZSTD_freeCCtx};
|
||||||
|
ZSTD_CCtx_setParameter(cctx.get(), ZSTD_c_compressionLevel, 0); // default (3)
|
||||||
|
ZSTD_CCtx_setParameter(cctx.get(), ZSTD_c_checksumFlag, 1); // add extra checksums (to frames?)
|
||||||
|
do {
|
||||||
|
buffer_actual_size = data_cb(buffer.data(), buffer.size());
|
||||||
|
//if (buffer_actual_size == 0) {
|
||||||
|
//break;
|
||||||
|
//}
|
||||||
|
if (buffer_actual_size > buffer.size()) {
|
||||||
|
// wtf
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bool const lastChunk = (buffer_actual_size < buffer.size());
|
||||||
|
|
||||||
|
ZSTD_EndDirective const mode = lastChunk ? ZSTD_e_end : ZSTD_e_continue;
|
||||||
|
ZSTD_inBuffer input = { buffer.data(), buffer_actual_size, 0 };
|
||||||
|
|
||||||
|
while (input.pos < input.size) {
|
||||||
|
ZSTD_outBuffer output = { compressed_buffer.data(), compressed_buffer.size(), 0 };
|
||||||
|
|
||||||
|
size_t const remaining = ZSTD_compressStream2(cctx.get(), &output , &input, mode);
|
||||||
|
if (ZSTD_isError(remaining)) {
|
||||||
|
std::cerr << "FS error: compressing data failed\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_file.write(reinterpret_cast<const char*>(compressed_buffer.data()), output.pos);
|
||||||
|
|
||||||
|
if (remaining == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// same as if lastChunk break;
|
||||||
|
} while (buffer_actual_size == buffer.size());
|
||||||
|
} else {
|
||||||
|
assert(false && "implement me");
|
||||||
|
}
|
||||||
|
|
||||||
|
meta_file.flush();
|
||||||
|
meta_file.close();
|
||||||
|
data_file.flush();
|
||||||
|
data_file.close();
|
||||||
|
|
||||||
|
std::filesystem::rename(
|
||||||
|
meta_tmp_path,
|
||||||
|
_reg.get<FragComp::Ephemeral::FilePath>(fid).path + ".meta" + metaFileTypeSuffix(meta_type)
|
||||||
|
);
|
||||||
|
|
||||||
|
std::filesystem::rename(
|
||||||
|
data_tmp_path,
|
||||||
|
_reg.get<FragComp::Ephemeral::FilePath>(fid).path
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: check return value of renames
|
||||||
|
|
||||||
|
if (_reg.all_of<FragComp::Ephemeral::DirtyTag>(fid)) {
|
||||||
|
_reg.remove<FragComp::Ephemeral::DirtyTag>(fid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FragmentStore::syncToStorage(FragmentID fid, const uint8_t* data, const uint64_t data_size) {
|
||||||
|
std::function<FragmentStore::write_to_storage_fetch_data_cb> fn_cb = [read = 0ull, data, data_size](uint8_t* request_buffer, uint64_t buffer_size) mutable -> uint64_t {
|
||||||
|
uint64_t i = 0;
|
||||||
|
for (; i+read < data_size && i < buffer_size; i++) {
|
||||||
|
request_buffer[i] = data[i+read];
|
||||||
|
}
|
||||||
|
read += i;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
return syncToStorage(fid, fn_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FragmentStore::loadFromStorage(FragmentID fid, std::function<read_from_storage_put_data_cb>& data_cb) {
|
||||||
|
if (!_reg.valid(fid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_reg.all_of<FragComp::Ephemeral::FilePath>(fid)) {
|
||||||
|
// not a file fragment?
|
||||||
|
// TODO: memory fragments
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& frag_path = _reg.get<FragComp::Ephemeral::FilePath>(fid).path;
|
||||||
|
|
||||||
|
// TODO: check if metadata dirty?
|
||||||
|
// TODO: what if file changed on disk?
|
||||||
|
|
||||||
|
std::cout << "FS: loading fragment '" << frag_path << "'\n";
|
||||||
|
|
||||||
|
std::ifstream data_file{
|
||||||
|
frag_path,
|
||||||
|
std::ios::in | std::ios::binary // always binary, also for text
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!data_file.is_open()) {
|
||||||
|
std::cerr << "FS error: fragment data file failed to open '" << frag_path << "'\n";
|
||||||
|
// error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Compression data_comp = Compression::NONE;
|
||||||
|
if (_reg.all_of<FragComp::DataCompressionType>(fid)) {
|
||||||
|
data_comp = _reg.get<FragComp::DataCompressionType>(fid).comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_comp == Compression::NONE) {
|
||||||
|
std::array<uint8_t, 1024> buffer;
|
||||||
|
uint64_t buffer_actual_size {0};
|
||||||
|
do {
|
||||||
|
data_file.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
|
||||||
|
buffer_actual_size = data_file.gcount();
|
||||||
|
|
||||||
|
if (buffer_actual_size == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_cb(buffer.data(), buffer_actual_size);
|
||||||
|
} while (buffer_actual_size == buffer.size() && !data_file.eof());
|
||||||
|
} else if (data_comp == Compression::ZSTD) {
|
||||||
|
std::vector<uint8_t> in_buffer(ZSTD_DStreamInSize());
|
||||||
|
std::vector<uint8_t> out_buffer(ZSTD_DStreamOutSize());
|
||||||
|
std::unique_ptr<ZSTD_DCtx, decltype(&ZSTD_freeDCtx)> dctx{ZSTD_createDCtx(), &ZSTD_freeDCtx};
|
||||||
|
|
||||||
|
uint64_t buffer_actual_size {0};
|
||||||
|
do {
|
||||||
|
data_file.read(reinterpret_cast<char*>(in_buffer.data()), in_buffer.size());
|
||||||
|
buffer_actual_size = data_file.gcount();
|
||||||
|
if (buffer_actual_size == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ZSTD_inBuffer input {in_buffer.data(), buffer_actual_size, 0 };
|
||||||
|
do {
|
||||||
|
ZSTD_outBuffer output = { out_buffer.data(), out_buffer.size(), 0 };
|
||||||
|
size_t const ret = ZSTD_decompressStream(dctx.get(), &output , &input);
|
||||||
|
if (ZSTD_isError(ret)) {
|
||||||
|
// error <.<
|
||||||
|
std::cerr << "FS error: decompression error\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
data_cb(out_buffer.data(), output.pos);
|
||||||
|
} while (input.pos < input.size);
|
||||||
|
} while (buffer_actual_size == in_buffer.size() && !data_file.eof());
|
||||||
|
} else {
|
||||||
|
assert(false && "implement me");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json FragmentStore::loadFromStorageNJ(FragmentID fid) {
|
||||||
|
std::vector<uint8_t> tmp_buffer;
|
||||||
|
std::function<read_from_storage_put_data_cb> cb = [&tmp_buffer](const uint8_t* buffer, const uint64_t buffer_size) {
|
||||||
|
tmp_buffer.insert(tmp_buffer.end(), buffer, buffer+buffer_size);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!loadFromStorage(fid, cb)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nlohmann::json::parse(tmp_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FragmentStore::scanStoragePath(std::string_view path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
path = _default_store_path;
|
||||||
|
}
|
||||||
|
// TODO: extract so async can work (or/and make iteratable generator)
|
||||||
|
|
||||||
|
if (!std::filesystem::is_directory(path)) {
|
||||||
|
std::cerr << "FS error: scan path not a directory '" << path << "'\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 1: make snapshot of files, validate metafiles and save id/path+meta.ext
|
||||||
|
// can be extra thread (if non vfs)
|
||||||
|
struct FragFileEntry {
|
||||||
|
std::string id_str;
|
||||||
|
std::filesystem::path frag_path;
|
||||||
|
std::string meta_ext;
|
||||||
|
|
||||||
|
bool operator==(const FragFileEntry& other) const {
|
||||||
|
// only compare by id
|
||||||
|
return id_str == other.id_str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct FragFileEntryHash {
|
||||||
|
size_t operator()(const FragFileEntry& it) const {
|
||||||
|
return entt::hashed_string(it.id_str.data(), it.id_str.size());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
entt::dense_set<FragFileEntry, FragFileEntryHash> file_frag_list;
|
||||||
|
|
||||||
|
std::filesystem::path storage_path{path};
|
||||||
|
|
||||||
|
auto handle_file = [&](const std::filesystem::path& file_path) {
|
||||||
|
if (!std::filesystem::is_regular_file(file_path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// handle file
|
||||||
|
|
||||||
|
if (file_path.has_extension()) {
|
||||||
|
// skip over metadata, assuming only metafiles have extentions (might be wrong?)
|
||||||
|
// also skips temps
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto relative_path = std::filesystem::proximate(file_path, storage_path);
|
||||||
|
std::string id_str = relative_path.generic_u8string();
|
||||||
|
// delete all '/'
|
||||||
|
id_str.erase(std::remove(id_str.begin(), id_str.end(), '/'), id_str.end());
|
||||||
|
if (id_str.size() % 2 != 0) {
|
||||||
|
std::cerr << "FS error: non hex fragment uid detected: '" << id_str << "'\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_frag_list.contains(FragFileEntry{id_str, {}, ""})) {
|
||||||
|
std::cerr << "FS error: fragment duplicate detected: '" << id_str << "'\n";
|
||||||
|
return; // skip
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* meta_ext = ".meta.msgpack";
|
||||||
|
{ // find meta
|
||||||
|
// TODO: this as to know all possible extentions
|
||||||
|
bool has_meta_msgpack = std::filesystem::is_regular_file(file_path.generic_u8string() + ".meta.msgpack");
|
||||||
|
bool has_meta_json = std::filesystem::is_regular_file(file_path.generic_u8string() + ".meta.json");
|
||||||
|
const size_t meta_sum =
|
||||||
|
(has_meta_msgpack?1:0) +
|
||||||
|
(has_meta_json?1:0)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (meta_sum > 1) { // has multiple
|
||||||
|
std::cerr << "FS error: fragment with multiple meta files detected: " << id_str << "\n";
|
||||||
|
return; // skip
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta_sum == 0) {
|
||||||
|
std::cerr << "FS error: fragment missing meta file detected: " << id_str << "\n";
|
||||||
|
return; // skip
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_meta_json) {
|
||||||
|
meta_ext = ".meta.json";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file_frag_list.emplace(FragFileEntry{
|
||||||
|
std::move(id_str),
|
||||||
|
file_path,
|
||||||
|
meta_ext
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& outer_path : std::filesystem::directory_iterator(storage_path)) {
|
||||||
|
if (std::filesystem::is_regular_file(outer_path)) {
|
||||||
|
handle_file(outer_path);
|
||||||
|
} else if (std::filesystem::is_directory(outer_path)) {
|
||||||
|
// subdir, part of id
|
||||||
|
for (const auto& inner_path : std::filesystem::directory_iterator(outer_path)) {
|
||||||
|
//if (std::filesystem::is_regular_file(inner_path)) {
|
||||||
|
|
||||||
|
//// handle file
|
||||||
|
//} // TODO: support deeper recursion?
|
||||||
|
handle_file(inner_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "FS: scan found:\n";
|
||||||
|
for (const auto& it : file_frag_list) {
|
||||||
|
std::cout << " " << it.id_str << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 2: check if files preexist in reg
|
||||||
|
// main thread
|
||||||
|
// (merge into step 3 and do more error checking?)
|
||||||
|
for (auto it = file_frag_list.begin(); it != file_frag_list.end();) {
|
||||||
|
auto id = hex2bin(it->id_str);
|
||||||
|
auto fid = getFragmentByID(id);
|
||||||
|
if (_reg.valid(fid)) {
|
||||||
|
// pre exising (handle differently??)
|
||||||
|
// check if store differs?
|
||||||
|
it = file_frag_list.erase(it);
|
||||||
|
} else {
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<FragmentID> scanned_frags;
|
||||||
|
// step 3: parse meta and insert into reg of non preexising
|
||||||
|
// main thread
|
||||||
|
// TODO: check timestamps of preexisting and reload? mark external/remote dirty?
|
||||||
|
for (const auto& it : file_frag_list) {
|
||||||
|
nlohmann::json j;
|
||||||
|
if (it.meta_ext == ".meta.msgpack") {
|
||||||
|
std::ifstream file(it.frag_path.generic_u8string() + it.meta_ext, std::ios::in | std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cout << "FS error: failed opening meta " << it.frag_path << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// file is a msgpack within a msgpack
|
||||||
|
|
||||||
|
std::vector<uint8_t> full_meta_data;
|
||||||
|
{ // read meta file
|
||||||
|
// figure out size
|
||||||
|
file.seekg(0, file.end);
|
||||||
|
uint64_t file_size = file.tellg();
|
||||||
|
file.seekg(0, file.beg);
|
||||||
|
|
||||||
|
full_meta_data.resize(file_size);
|
||||||
|
|
||||||
|
file.read(reinterpret_cast<char*>(full_meta_data.data()), full_meta_data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto meta_header_j = nlohmann::json::from_msgpack(full_meta_data);
|
||||||
|
|
||||||
|
if (!meta_header_j.is_array() || meta_header_j.size() < 4) {
|
||||||
|
std::cerr << "FS error: broken binary meta " << it.frag_path << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta_header_j.at(0) != "SOLMET") {
|
||||||
|
std::cerr << "FS error: wrong magic '" << meta_header_j.at(0) << "' in meta " << it.frag_path << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Encryption meta_enc = meta_header_j.at(1);
|
||||||
|
if (meta_enc != Encryption::NONE) {
|
||||||
|
std::cerr << "FS error: unknown encryption " << it.frag_path << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Compression meta_comp = meta_header_j.at(2);
|
||||||
|
if (meta_comp != Compression::NONE && meta_comp != Compression::ZSTD) {
|
||||||
|
std::cerr << "FS error: unknown compression " << it.frag_path << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//const auto& meta_data_ref = meta_header_j.at(3).is_binary()?meta_header_j.at(3):meta_header_j.at(3).at("data");
|
||||||
|
if (!meta_header_j.at(3).is_binary()) {
|
||||||
|
std::cerr << "FS error: meta data not binary " << it.frag_path << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const nlohmann::json::binary_t& meta_data_ref = meta_header_j.at(3);
|
||||||
|
|
||||||
|
std::vector<uint8_t> meta_data_decomp;
|
||||||
|
if (meta_comp == Compression::NONE) {
|
||||||
|
// do nothing
|
||||||
|
} else if (meta_comp == Compression::ZSTD) {
|
||||||
|
meta_data_decomp.resize(ZSTD_DStreamOutSize());
|
||||||
|
std::unique_ptr<ZSTD_DCtx, decltype(&ZSTD_freeDCtx)> dctx{ZSTD_createDCtx(), &ZSTD_freeDCtx};
|
||||||
|
|
||||||
|
ZSTD_inBuffer input {meta_data_ref.data(), meta_data_ref.size(), 0};
|
||||||
|
ZSTD_outBuffer output = {meta_data_decomp.data(), meta_data_decomp.size(), 0};
|
||||||
|
do {
|
||||||
|
size_t const ret = ZSTD_decompressStream(dctx.get(), &output , &input);
|
||||||
|
if (ZSTD_isError(ret)) {
|
||||||
|
// error <.<
|
||||||
|
std::cerr << "FS error: decompression error\n";
|
||||||
|
//meta_data_decomp.clear();
|
||||||
|
output.pos = 0; // resize after loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (input.pos < input.size);
|
||||||
|
meta_data_decomp.resize(output.pos);
|
||||||
|
} else {
|
||||||
|
assert(false && "implement me");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: enc
|
||||||
|
|
||||||
|
if (!meta_data_decomp.empty()) {
|
||||||
|
j = nlohmann::json::from_msgpack(meta_data_decomp);
|
||||||
|
} else {
|
||||||
|
j = nlohmann::json::from_msgpack(meta_data_ref);
|
||||||
|
}
|
||||||
|
} else if (it.meta_ext == ".meta.json") {
|
||||||
|
std::ifstream file(it.frag_path.generic_u8string() + it.meta_ext, std::ios::in | std::ios::binary);
|
||||||
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "FS error: failed opening meta " << it.frag_path << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
file >> j;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!j.is_object()) {
|
||||||
|
std::cerr << "FS error: json in meta is broken " << it.id_str << "\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: existing fragment file
|
||||||
|
//newFragmentFile();
|
||||||
|
FragmentHandle fh{_reg, _reg.create()};
|
||||||
|
fh.emplace<FragComp::ID>(hex2bin(it.id_str));
|
||||||
|
|
||||||
|
fh.emplace<FragComp::Ephemeral::FilePath>(it.frag_path.generic_u8string());
|
||||||
|
|
||||||
|
for (const auto& [k, v] : j.items()) {
|
||||||
|
// type id from string hash
|
||||||
|
const auto type_id = entt::hashed_string(k.data(), k.size());
|
||||||
|
const auto deserl_fn_it = _sc._deserl_json.find(type_id);
|
||||||
|
if (deserl_fn_it != _sc._deserl_json.cend()) {
|
||||||
|
// TODO: check return value
|
||||||
|
deserl_fn_it->second(fh, v);
|
||||||
|
} else {
|
||||||
|
std::cerr << "FS warning: missing deserializer for meta key '" << k << "'\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scanned_frags.push_back(fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: mutex and move code to async and return this list ?
|
||||||
|
|
||||||
|
// throw new frag event here, after loading them all
|
||||||
|
for (const FragmentID fid : scanned_frags) {
|
||||||
|
throwEventConstruct(fid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scanned_frags.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FragmentStore::scanStoragePathAsync(std::string path) {
|
||||||
|
// add path to queue
|
||||||
|
// HACK: if path is known/any fragment is in the path, this operation blocks (non async)
|
||||||
|
scanStoragePath(path); // TODO: make async and post result
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool serl_json_data_enc_type(const FragmentHandle fh, nlohmann::json& out) {
|
||||||
|
out = static_cast<std::underlying_type_t<Encryption>>(
|
||||||
|
fh.get<FragComp::DataEncryptionType>().enc
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool deserl_json_data_enc_type(FragmentHandle fh, const nlohmann::json& in) {
|
||||||
|
fh.emplace_or_replace<FragComp::DataEncryptionType>(
|
||||||
|
static_cast<Encryption>(
|
||||||
|
static_cast<std::underlying_type_t<Encryption>>(in)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool serl_json_data_comp_type(const FragmentHandle fh, nlohmann::json& out) {
|
||||||
|
out = static_cast<std::underlying_type_t<Compression>>(
|
||||||
|
fh.get<FragComp::DataCompressionType>().comp
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool deserl_json_data_comp_type(FragmentHandle fh, const nlohmann::json& in) {
|
||||||
|
fh.emplace_or_replace<FragComp::DataCompressionType>(
|
||||||
|
static_cast<Compression>(
|
||||||
|
static_cast<std::underlying_type_t<Compression>>(in)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FragmentStore::registerSerializers(void) {
|
||||||
|
_sc.registerSerializerJson<FragComp::DataEncryptionType>(serl_json_data_enc_type);
|
||||||
|
_sc.registerDeSerializerJson<FragComp::DataEncryptionType>(deserl_json_data_enc_type);
|
||||||
|
_sc.registerSerializerJson<FragComp::DataCompressionType>(serl_json_data_comp_type);
|
||||||
|
_sc.registerDeSerializerJson<FragComp::DataCompressionType>(deserl_json_data_comp_type);
|
||||||
|
}
|
||||||
|
|
103
src/fragment_store/fragment_store.hpp
Normal file
103
src/fragment_store/fragment_store.hpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./fragment_store_i.hpp"
|
||||||
|
|
||||||
|
#include "./types.hpp"
|
||||||
|
#include "./meta_components.hpp"
|
||||||
|
|
||||||
|
#include "./serializer.hpp"
|
||||||
|
|
||||||
|
#include <entt/core/type_info.hpp>
|
||||||
|
#include <entt/entity/registry.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
struct FragmentStore : public FragmentStoreI {
|
||||||
|
std::minstd_rand _rng{std::random_device{}()};
|
||||||
|
std::array<uint8_t, 16> _session_uuid_namespace;
|
||||||
|
|
||||||
|
std::string _default_store_path;
|
||||||
|
|
||||||
|
uint64_t _memory_budget {10u*1024u*1024u};
|
||||||
|
uint64_t _memory_usage {0u};
|
||||||
|
|
||||||
|
SerializerCallbacks<FragmentID> _sc;
|
||||||
|
|
||||||
|
FragmentStore(void);
|
||||||
|
FragmentStore(std::array<uint8_t, 16> session_uuid_namespace);
|
||||||
|
|
||||||
|
// HACK: get access to the reg
|
||||||
|
FragmentHandle fragmentHandle(FragmentID fid);
|
||||||
|
|
||||||
|
// TODO: make the frags ref counted
|
||||||
|
|
||||||
|
// TODO: check for exising
|
||||||
|
std::vector<uint8_t> generateNewUID(std::array<uint8_t, 16>& uuid_namespace);
|
||||||
|
std::vector<uint8_t> generateNewUID(void);
|
||||||
|
|
||||||
|
// ========== new fragment ==========
|
||||||
|
|
||||||
|
// memory backed owned
|
||||||
|
FragmentID newFragmentMemoryOwned(
|
||||||
|
const std::vector<uint8_t>& id,
|
||||||
|
size_t initial_size
|
||||||
|
);
|
||||||
|
|
||||||
|
// memory backed view (can only be added? not new?)
|
||||||
|
|
||||||
|
// file backed (rw...)
|
||||||
|
// needs to know which store path to put into
|
||||||
|
FragmentID newFragmentFile(
|
||||||
|
std::string_view store_path,
|
||||||
|
MetaFileType mft,
|
||||||
|
const std::vector<uint8_t>& id
|
||||||
|
);
|
||||||
|
// this variant generate a new, mostly unique, id for us
|
||||||
|
FragmentID newFragmentFile(
|
||||||
|
std::string_view store_path,
|
||||||
|
MetaFileType mft
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========== add fragment ==========
|
||||||
|
|
||||||
|
// ========== get fragment ==========
|
||||||
|
FragmentID getFragmentByID(
|
||||||
|
const std::vector<uint8_t>& id
|
||||||
|
);
|
||||||
|
FragmentID getFragmentCustomMatcher(
|
||||||
|
std::function<bool(FragmentID)>& fn
|
||||||
|
);
|
||||||
|
|
||||||
|
// remove fragment?
|
||||||
|
// unload?
|
||||||
|
|
||||||
|
// ========== sync fragment to storage ==========
|
||||||
|
using write_to_storage_fetch_data_cb = uint64_t(uint8_t* request_buffer, uint64_t buffer_size);
|
||||||
|
// calls data_cb with a buffer to be filled in, cb returns actual count of data. if returned < max, its the last buffer.
|
||||||
|
bool syncToStorage(FragmentID fid, std::function<write_to_storage_fetch_data_cb>& data_cb);
|
||||||
|
bool syncToStorage(FragmentID fid, const uint8_t* data, const uint64_t data_size);
|
||||||
|
|
||||||
|
// ========== load fragment data from storage ==========
|
||||||
|
using read_from_storage_put_data_cb = void(const uint8_t* buffer, const uint64_t buffer_size);
|
||||||
|
bool loadFromStorage(FragmentID fid, std::function<read_from_storage_put_data_cb>& data_cb);
|
||||||
|
// convenience function
|
||||||
|
nlohmann::json loadFromStorageNJ(FragmentID fid);
|
||||||
|
|
||||||
|
// fragment discovery?
|
||||||
|
// returns number of new fragments
|
||||||
|
size_t scanStoragePath(std::string_view path);
|
||||||
|
void scanStoragePathAsync(std::string path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void registerSerializers(void); // internal comps
|
||||||
|
// internal actual backends
|
||||||
|
// TODO: seperate out
|
||||||
|
bool syncToMemory(FragmentID fid, std::function<write_to_storage_fetch_data_cb>& data_cb);
|
||||||
|
bool syncToFile(FragmentID fid, std::function<write_to_storage_fetch_data_cb>& data_cb);
|
||||||
|
};
|
||||||
|
|
23
src/fragment_store/fragment_store_i.cpp
Normal file
23
src/fragment_store/fragment_store_i.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "./fragment_store_i.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void FragmentStoreI::throwEventConstruct(const FragmentID fid) {
|
||||||
|
std::cout << "FSI debug: event construct " << entt::to_integral(fid) << "\n";
|
||||||
|
dispatch(
|
||||||
|
FragmentStore_Event::fragment_construct,
|
||||||
|
Fragment::Events::FragmentConstruct{
|
||||||
|
FragmentHandle{_reg, fid}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FragmentStoreI::throwEventUpdate(const FragmentID fid) {
|
||||||
|
std::cout << "FSI debug: event updated " << entt::to_integral(fid) << "\n";
|
||||||
|
dispatch(
|
||||||
|
FragmentStore_Event::fragment_updated,
|
||||||
|
Fragment::Events::FragmentUpdated{
|
||||||
|
FragmentHandle{_reg, fid}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
63
src/fragment_store/fragment_store_i.hpp
Normal file
63
src/fragment_store/fragment_store_i.hpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/util/event_provider.hpp>
|
||||||
|
|
||||||
|
#include <entt/entity/registry.hpp>
|
||||||
|
#include <entt/entity/handle.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// internal id
|
||||||
|
enum class FragmentID : uint32_t {};
|
||||||
|
using FragmentRegistry = entt::basic_registry<FragmentID>;
|
||||||
|
using FragmentHandle = entt::basic_handle<FragmentRegistry>;
|
||||||
|
|
||||||
|
namespace Fragment::Events {
|
||||||
|
|
||||||
|
struct FragmentConstruct {
|
||||||
|
const FragmentHandle e;
|
||||||
|
};
|
||||||
|
struct FragmentUpdated {
|
||||||
|
const FragmentHandle e;
|
||||||
|
};
|
||||||
|
//struct MessageDestory {
|
||||||
|
//const Message3Handle e;
|
||||||
|
//};
|
||||||
|
|
||||||
|
} // Fragment::Events
|
||||||
|
|
||||||
|
enum class FragmentStore_Event : uint32_t {
|
||||||
|
fragment_construct,
|
||||||
|
fragment_updated,
|
||||||
|
//message_destroy,
|
||||||
|
|
||||||
|
MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FragmentStoreEventI {
|
||||||
|
using enumType = FragmentStore_Event;
|
||||||
|
|
||||||
|
virtual ~FragmentStoreEventI(void) {}
|
||||||
|
|
||||||
|
virtual bool onEvent(const Fragment::Events::FragmentConstruct&) { return false; }
|
||||||
|
virtual bool onEvent(const Fragment::Events::FragmentUpdated&) { return false; }
|
||||||
|
//virtual bool onEvent(const Fragment::Events::MessageDestory&) { return false; }
|
||||||
|
|
||||||
|
// mm3
|
||||||
|
// send text
|
||||||
|
// send file path
|
||||||
|
};
|
||||||
|
using FragmentStoreEventProviderI = EventProviderI<FragmentStoreEventI>;
|
||||||
|
|
||||||
|
struct FragmentStoreI : public FragmentStoreEventProviderI {
|
||||||
|
static constexpr const char* version {"1"};
|
||||||
|
|
||||||
|
FragmentRegistry _reg;
|
||||||
|
|
||||||
|
virtual ~FragmentStoreI(void) {}
|
||||||
|
|
||||||
|
void throwEventConstruct(const FragmentID fid);
|
||||||
|
void throwEventUpdate(const FragmentID fid);
|
||||||
|
//void throwEventDestroy();
|
||||||
|
};
|
||||||
|
|
BIN
src/fragment_store/fs_binary_msgpack1.png
Normal file
BIN
src/fragment_store/fs_binary_msgpack1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
src/fragment_store/fs_binary_msgpack2.png
Normal file
BIN
src/fragment_store/fs_binary_msgpack2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
900
src/fragment_store/message_fragment_store.cpp
Normal file
900
src/fragment_store/message_fragment_store.cpp
Normal file
@ -0,0 +1,900 @@
|
|||||||
|
#include "./message_fragment_store.hpp"
|
||||||
|
|
||||||
|
#include "../json/message_components.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/util/utils.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
#include <solanaceae/message3/components.hpp>
|
||||||
|
#include <solanaceae/message3/contact_components.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// https://youtu.be/CU2exyhYPfA
|
||||||
|
|
||||||
|
// everything assumes a single fragment registry
|
||||||
|
|
||||||
|
namespace Message::Components {
|
||||||
|
|
||||||
|
// ctx
|
||||||
|
struct OpenFragments {
|
||||||
|
//struct OpenFrag final {
|
||||||
|
////std::vector<uint8_t> uid;
|
||||||
|
//FragmentID id;
|
||||||
|
//};
|
||||||
|
// only contains fragments with <1024 messages and <28h tsrage (or whatever)
|
||||||
|
entt::dense_set<FragmentID> fid_open;
|
||||||
|
};
|
||||||
|
|
||||||
|
// all message fragments of this contact
|
||||||
|
struct ContactFragments final {
|
||||||
|
// kept up-to-date by events
|
||||||
|
struct InternalEntry {
|
||||||
|
// indecies into the sorted arrays
|
||||||
|
size_t i_b;
|
||||||
|
size_t i_e;
|
||||||
|
};
|
||||||
|
entt::dense_map<FragmentID, InternalEntry> frags;
|
||||||
|
|
||||||
|
// add 2 sorted contact lists for both range begin and end
|
||||||
|
// TODO: adding and removing becomes expensive with enough frags, consider splitting or heap
|
||||||
|
std::vector<FragmentID> sorted_begin;
|
||||||
|
std::vector<FragmentID> sorted_end;
|
||||||
|
|
||||||
|
// api
|
||||||
|
// return true if it was actually inserted
|
||||||
|
bool insert(FragmentHandle frag);
|
||||||
|
bool erase(FragmentID frag);
|
||||||
|
// update? (just erase() + insert())
|
||||||
|
|
||||||
|
// uses range begin to go back in time
|
||||||
|
FragmentID prev(FragmentID frag) const;
|
||||||
|
// uses range end to go forward in time
|
||||||
|
FragmentID next(FragmentID frag) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// all LOADED message fragments
|
||||||
|
// TODO: merge into ContactFragments (and pull in openfrags)
|
||||||
|
struct LoadedContactFragments final {
|
||||||
|
// kept up-to-date by events
|
||||||
|
entt::dense_set<FragmentID> frags;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Message::Components
|
||||||
|
|
||||||
|
namespace Fragment::Components {
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MessagesTSRange, begin, end)
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MessagesContact, id)
|
||||||
|
} // Fragment::Components
|
||||||
|
|
||||||
|
void MessageFragmentStore::handleMessage(const Message3Handle& m) {
|
||||||
|
if (_fs_ignore_event) {
|
||||||
|
// message event because of us loading a fragment, ignore
|
||||||
|
// TODO: this barely makes a difference
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!static_cast<bool>(m)) {
|
||||||
|
return; // huh?
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m.all_of<Message::Components::Timestamp>()) {
|
||||||
|
return; // we only handle msg with ts
|
||||||
|
}
|
||||||
|
|
||||||
|
_potentially_dirty_contacts.emplace(m.registry()->ctx().get<Contact3>()); // always mark dirty here
|
||||||
|
if (m.any_of<Message::Components::ViewCurserBegin, Message::Components::ViewCurserEnd>()) {
|
||||||
|
// not an actual message, but we probalby need to check and see if we need to load fragments
|
||||||
|
//std::cout << "MFS: new or updated curser\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: use fid, seving full fuid for every message consumes alot of memory (and heap frag)
|
||||||
|
if (!m.all_of<Message::Components::FID>()) {
|
||||||
|
std::cout << "MFS: new msg missing FID\n";
|
||||||
|
if (!m.registry()->ctx().contains<Message::Components::OpenFragments>()) {
|
||||||
|
m.registry()->ctx().emplace<Message::Components::OpenFragments>();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& fid_open = m.registry()->ctx().get<Message::Components::OpenFragments>().fid_open;
|
||||||
|
|
||||||
|
const auto msg_ts = m.get<Message::Components::Timestamp>().ts;
|
||||||
|
// missing fuid
|
||||||
|
// find closesed non-sealed off fragment
|
||||||
|
|
||||||
|
FragmentID fragment_id{entt::null};
|
||||||
|
|
||||||
|
// first search for fragment where the ts falls into the range
|
||||||
|
for (const auto& fid : fid_open) {
|
||||||
|
auto fh = _fs.fragmentHandle(fid);
|
||||||
|
assert(static_cast<bool>(fh));
|
||||||
|
|
||||||
|
// assuming ts range exists
|
||||||
|
auto& fts_comp = fh.get<FragComp::MessagesTSRange>();
|
||||||
|
|
||||||
|
if (fts_comp.begin <= msg_ts && fts_comp.end >= msg_ts) {
|
||||||
|
fragment_id = fid;
|
||||||
|
// TODO: check conditions for open here
|
||||||
|
// TODO: mark msg (and frag?) dirty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it did not fit into an existing fragment, we next look for fragments that could be extended
|
||||||
|
if (!_fs._reg.valid(fragment_id)) {
|
||||||
|
for (const auto& fid : fid_open) {
|
||||||
|
auto fh = _fs.fragmentHandle(fid);
|
||||||
|
assert(static_cast<bool>(fh));
|
||||||
|
|
||||||
|
// assuming ts range exists
|
||||||
|
auto& fts_comp = fh.get<FragComp::MessagesTSRange>();
|
||||||
|
|
||||||
|
const int64_t frag_range = int64_t(fts_comp.end) - int64_t(fts_comp.begin);
|
||||||
|
constexpr static int64_t max_frag_ts_extent {1000*60*60};
|
||||||
|
//constexpr static int64_t max_frag_ts_extent {1000*60*3}; // 3min for testing
|
||||||
|
const int64_t possible_extention = max_frag_ts_extent - frag_range;
|
||||||
|
|
||||||
|
// which direction
|
||||||
|
if ((fts_comp.begin - possible_extention) <= msg_ts && fts_comp.begin > msg_ts) {
|
||||||
|
fragment_id = fid;
|
||||||
|
|
||||||
|
std::cout << "MFS: extended begin from " << fts_comp.begin << " to " << msg_ts << "\n";
|
||||||
|
|
||||||
|
// assuming ts range exists
|
||||||
|
fts_comp.begin = msg_ts; // extend into the past
|
||||||
|
|
||||||
|
if (m.registry()->ctx().contains<Message::Components::ContactFragments>()) {
|
||||||
|
// should be the case
|
||||||
|
m.registry()->ctx().get<Message::Components::ContactFragments>().erase(fh);
|
||||||
|
m.registry()->ctx().get<Message::Components::ContactFragments>().insert(fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: check conditions for open here
|
||||||
|
// TODO: mark msg (and frag?) dirty
|
||||||
|
} else if ((fts_comp.end + possible_extention) >= msg_ts && fts_comp.end < msg_ts) {
|
||||||
|
fragment_id = fid;
|
||||||
|
|
||||||
|
std::cout << "MFS: extended end from " << fts_comp.end << " to " << msg_ts << "\n";
|
||||||
|
|
||||||
|
// assuming ts range exists
|
||||||
|
fts_comp.end = msg_ts; // extend into the future
|
||||||
|
|
||||||
|
if (m.registry()->ctx().contains<Message::Components::ContactFragments>()) {
|
||||||
|
// should be the case
|
||||||
|
m.registry()->ctx().get<Message::Components::ContactFragments>().erase(fh);
|
||||||
|
m.registry()->ctx().get<Message::Components::ContactFragments>().insert(fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check conditions for open here
|
||||||
|
// TODO: mark msg (and frag?) dirty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if its still not found, we need a new fragment
|
||||||
|
if (!_fs._reg.valid(fragment_id)) {
|
||||||
|
const auto new_fid = _fs.newFragmentFile("test_message_store/", MetaFileType::BINARY_MSGPACK);
|
||||||
|
auto fh = _fs.fragmentHandle(new_fid);
|
||||||
|
if (!static_cast<bool>(fh)) {
|
||||||
|
std::cout << "MFS error: failed to create new fragment for message\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment_id = fh;
|
||||||
|
|
||||||
|
fh.emplace_or_replace<FragComp::Ephemeral::MetaCompressionType>().comp = Compression::ZSTD;
|
||||||
|
fh.emplace_or_replace<FragComp::DataCompressionType>().comp = Compression::ZSTD;
|
||||||
|
|
||||||
|
auto& new_ts_range = fh.emplace_or_replace<FragComp::MessagesTSRange>();
|
||||||
|
new_ts_range.begin = msg_ts;
|
||||||
|
new_ts_range.end = msg_ts;
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto msg_reg_contact = m.registry()->ctx().get<Contact3>();
|
||||||
|
if (_cr.all_of<Contact::Components::ID>(msg_reg_contact)) {
|
||||||
|
fh.emplace<FragComp::MessagesContact>(_cr.get<Contact::Components::ID>(msg_reg_contact).data);
|
||||||
|
} else {
|
||||||
|
// ? rage quit?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// contact frag
|
||||||
|
if (!m.registry()->ctx().contains<Message::Components::ContactFragments>()) {
|
||||||
|
m.registry()->ctx().emplace<Message::Components::ContactFragments>();
|
||||||
|
}
|
||||||
|
m.registry()->ctx().get<Message::Components::ContactFragments>().insert(fh);
|
||||||
|
|
||||||
|
// loaded contact frag
|
||||||
|
if (!m.registry()->ctx().contains<Message::Components::LoadedContactFragments>()) {
|
||||||
|
m.registry()->ctx().emplace<Message::Components::LoadedContactFragments>();
|
||||||
|
}
|
||||||
|
m.registry()->ctx().get<Message::Components::LoadedContactFragments>().frags.emplace(fh);
|
||||||
|
|
||||||
|
fid_open.emplace(fragment_id);
|
||||||
|
|
||||||
|
std::cout << "MFS: created new fragment " << bin2hex(fh.get<FragComp::ID>().v) << "\n";
|
||||||
|
|
||||||
|
_fs_ignore_event = true;
|
||||||
|
_fs.throwEventConstruct(fh);
|
||||||
|
_fs_ignore_event = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this is still empty, something is very wrong and we exit here
|
||||||
|
if (!_fs._reg.valid(fragment_id)) {
|
||||||
|
std::cout << "MFS error: failed to find/create fragment for message\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.emplace_or_replace<Message::Components::FID>(fragment_id);
|
||||||
|
|
||||||
|
// in this case we know the fragment needs an update
|
||||||
|
_fuid_save_queue.push({Message::getTimeMS(), fragment_id, m.registry()});
|
||||||
|
return; // done
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto msg_fh = _fs.fragmentHandle(m.get<Message::Components::FID>().fid);
|
||||||
|
if (!static_cast<bool>(msg_fh)) {
|
||||||
|
std::cerr << "MFS error: fid in message is invalid\n";
|
||||||
|
return; // TODO: properly handle this case
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m.registry()->ctx().contains<Message::Components::OpenFragments>()) {
|
||||||
|
m.registry()->ctx().emplace<Message::Components::OpenFragments>();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& fid_open = m.registry()->ctx().get<Message::Components::OpenFragments>().fid_open;
|
||||||
|
|
||||||
|
if (fid_open.contains(msg_fh)) {
|
||||||
|
// TODO: dedup events
|
||||||
|
// TODO: cooldown per fragsave
|
||||||
|
_fuid_save_queue.push({Message::getTimeMS(), msg_fh, m.registry()});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: save updates to old fragments, but writing them to a new fragment that would overwrite on merge
|
||||||
|
// new fragment?, since we dont write to others fragments?
|
||||||
|
|
||||||
|
|
||||||
|
// on new message: assign fuid
|
||||||
|
// on new and update: mark as fragment dirty
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes not loaded frag
|
||||||
|
// need update from frag
|
||||||
|
void MessageFragmentStore::loadFragment(Message3Registry& reg, FragmentHandle fh) {
|
||||||
|
std::cout << "MFS: loadFragment\n";
|
||||||
|
const auto j = _fs.loadFromStorageNJ(fh);
|
||||||
|
|
||||||
|
if (!j.is_array()) {
|
||||||
|
// wrong data
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this should probably never be the case, since we already know here that it is a msg frag
|
||||||
|
if (!reg.ctx().contains<Message::Components::ContactFragments>()) {
|
||||||
|
reg.ctx().emplace<Message::Components::ContactFragments>();
|
||||||
|
}
|
||||||
|
reg.ctx().get<Message::Components::ContactFragments>().insert(fh);
|
||||||
|
|
||||||
|
// mark loaded
|
||||||
|
if (!reg.ctx().contains<Message::Components::LoadedContactFragments>()) {
|
||||||
|
reg.ctx().emplace<Message::Components::LoadedContactFragments>();
|
||||||
|
}
|
||||||
|
reg.ctx().get<Message::Components::LoadedContactFragments>().frags.emplace(fh);
|
||||||
|
|
||||||
|
for (const auto& j_entry : j) {
|
||||||
|
auto new_real_msg = Message3Handle{reg, reg.create()};
|
||||||
|
// load into staging reg
|
||||||
|
for (const auto& [k, v] : j_entry.items()) {
|
||||||
|
//std::cout << "K:" << k << " V:" << v.dump() << "\n";
|
||||||
|
const auto type_id = entt::hashed_string(k.data(), k.size());
|
||||||
|
const auto deserl_fn_it = _sc._deserl_json.find(type_id);
|
||||||
|
if (deserl_fn_it != _sc._deserl_json.cend()) {
|
||||||
|
try {
|
||||||
|
if (!deserl_fn_it->second(_sc, new_real_msg, v)) {
|
||||||
|
std::cerr << "MFS error: failed deserializing '" << k << "'\n";
|
||||||
|
}
|
||||||
|
} catch(...) {
|
||||||
|
std::cerr << "MFS error: failed deserializing (threw) '" << k << "'\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "MFS warning: missing deserializer for meta key '" << k << "'\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_real_msg.emplace_or_replace<Message::Components::FID>(fh);
|
||||||
|
|
||||||
|
// dup check (hacky, specific to protocols)
|
||||||
|
Message3 dup_msg {entt::null};
|
||||||
|
{
|
||||||
|
// get comparator from contact
|
||||||
|
if (reg.ctx().contains<Contact3>()) {
|
||||||
|
const auto c = reg.ctx().get<Contact3>();
|
||||||
|
if (_cr.all_of<Contact::Components::MessageIsSame>(c)) {
|
||||||
|
auto& comp = _cr.get<Contact::Components::MessageIsSame>(c).comp;
|
||||||
|
// walking EVERY existing message OOF
|
||||||
|
// this needs optimizing
|
||||||
|
for (const Message3 other_msg : reg.view<Message::Components::Timestamp, Message::Components::ContactFrom, Message::Components::ContactTo>()) {
|
||||||
|
if (other_msg == new_real_msg) {
|
||||||
|
continue; // skip self
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp({reg, other_msg}, new_real_msg)) {
|
||||||
|
// dup
|
||||||
|
dup_msg = other_msg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reg.valid(dup_msg)) {
|
||||||
|
// -> merge with preexisting (needs to be order independent)
|
||||||
|
// -> throw update
|
||||||
|
reg.destroy(new_real_msg);
|
||||||
|
//_rmm.throwEventUpdate(reg, new_real_msg);
|
||||||
|
} else {
|
||||||
|
if (!new_real_msg.all_of<Message::Components::Timestamp, Message::Components::ContactFrom, Message::Components::ContactTo>()) {
|
||||||
|
// does not have needed components to be stand alone
|
||||||
|
reg.destroy(new_real_msg);
|
||||||
|
std::cerr << "MFS warning: message with missing basic compoments\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -> throw create
|
||||||
|
_rmm.throwEventConstruct(reg, new_real_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageFragmentStore::syncFragToStorage(FragmentHandle fh, Message3Registry& reg) {
|
||||||
|
auto& ftsrange = fh.get_or_emplace<FragComp::MessagesTSRange>(Message::getTimeMS(), Message::getTimeMS());
|
||||||
|
|
||||||
|
auto j = nlohmann::json::array();
|
||||||
|
|
||||||
|
// TODO: does every message have ts?
|
||||||
|
auto msg_view = reg.view<Message::Components::Timestamp>();
|
||||||
|
// we also assume all messages have fid
|
||||||
|
for (auto it = msg_view.rbegin(), it_end = msg_view.rend(); it != it_end; it++) {
|
||||||
|
const Message3 m = *it;
|
||||||
|
|
||||||
|
if (!reg.all_of<Message::Components::FID, Message::Components::ContactFrom, Message::Components::ContactTo>(m)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// require msg for now
|
||||||
|
if (!reg.any_of<Message::Components::MessageText/*, Message::Components::Transfer::FileInfo*/>(m)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_fuid_save_queue.front().id != reg.get<Message::Components::FID>(m).fid) {
|
||||||
|
continue; // not ours
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // potentially adjust tsrange (some external processes can change timestamps)
|
||||||
|
const auto msg_ts = msg_view.get<Message::Components::Timestamp>(m).ts;
|
||||||
|
if (ftsrange.begin > msg_ts) {
|
||||||
|
ftsrange.begin = msg_ts;
|
||||||
|
} else if (ftsrange.end < msg_ts) {
|
||||||
|
ftsrange.end = msg_ts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& j_entry = j.emplace_back(nlohmann::json::object());
|
||||||
|
|
||||||
|
for (const auto& [type_id, storage] : reg.storage()) {
|
||||||
|
if (!storage.contains(m)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout << "storage type: type_id:" << type_id << " name:" << storage.type().name() << "\n";
|
||||||
|
|
||||||
|
// use type_id to find serializer
|
||||||
|
auto s_cb_it = _sc._serl_json.find(type_id);
|
||||||
|
if (s_cb_it == _sc._serl_json.end()) {
|
||||||
|
// could not find serializer, not saving
|
||||||
|
//std::cout << "missing " << storage.type().name() << "(" << type_id << ")\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_cb_it->second(_sc, {reg, m}, j_entry[storage.type().name()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we cant skip if array is empty (in theory it will not be empty later on)
|
||||||
|
|
||||||
|
// if save as binary
|
||||||
|
//nlohmann::json::to_msgpack(j);
|
||||||
|
auto j_dump = j.dump(2, ' ', true);
|
||||||
|
if (_fs.syncToStorage(fh, reinterpret_cast<const uint8_t*>(j_dump.data()), j_dump.size())) {
|
||||||
|
//std::cout << "MFS: dumped " << j_dump << "\n";
|
||||||
|
// succ
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageFragmentStore::MessageFragmentStore(
|
||||||
|
Contact3Registry& cr,
|
||||||
|
RegistryMessageModel& rmm,
|
||||||
|
FragmentStore& fs
|
||||||
|
) : _cr(cr), _rmm(rmm), _fs(fs), _sc{_cr, {}, {}} {
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct);
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::message_updated);
|
||||||
|
_rmm.subscribe(this, RegistryMessageModel_Event::message_destroy);
|
||||||
|
|
||||||
|
_fs._sc.registerSerializerJson<FragComp::MessagesTSRange>();
|
||||||
|
_fs._sc.registerDeSerializerJson<FragComp::MessagesTSRange>();
|
||||||
|
_fs._sc.registerSerializerJson<FragComp::MessagesContact>();
|
||||||
|
_fs._sc.registerDeSerializerJson<FragComp::MessagesContact>();
|
||||||
|
|
||||||
|
_fs.subscribe(this, FragmentStore_Event::fragment_construct);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageFragmentStore::~MessageFragmentStore(void) {
|
||||||
|
while (!_fuid_save_queue.empty()) {
|
||||||
|
auto fh = _fs.fragmentHandle(_fuid_save_queue.front().id);
|
||||||
|
auto* reg = _fuid_save_queue.front().reg;
|
||||||
|
assert(reg != nullptr);
|
||||||
|
syncFragToStorage(fh, *reg);
|
||||||
|
_fuid_save_queue.pop(); // pop unconditionally
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageSerializerCallbacks& MessageFragmentStore::getMSC(void) {
|
||||||
|
return _sc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks range against all cursers in msgreg
|
||||||
|
static bool rangeVisible(uint64_t range_begin, uint64_t range_end, const Message3Registry& msg_reg) {
|
||||||
|
// 1D collision checks:
|
||||||
|
// - for range vs range:
|
||||||
|
// r1 rhs >= r0 lhs AND r1 lhs <= r0 rhs
|
||||||
|
// - for range vs point:
|
||||||
|
// p >= r0 lhs AND p <= r0 rhs
|
||||||
|
// NOTE: directions for us are reversed (begin has larger values as end)
|
||||||
|
|
||||||
|
auto c_b_view = msg_reg.view<Message::Components::Timestamp, Message::Components::ViewCurserBegin>();
|
||||||
|
c_b_view.use<Message::Components::ViewCurserBegin>();
|
||||||
|
for (const auto& [m, ts_begin_comp, vcb] : c_b_view.each()) {
|
||||||
|
// p and r1 rhs can be seen as the same
|
||||||
|
// but first we need to know if a curser begin is a point or a range
|
||||||
|
|
||||||
|
// TODO: margin?
|
||||||
|
auto ts_begin = ts_begin_comp.ts;
|
||||||
|
auto ts_end = ts_begin_comp.ts; // simplyfy code by making a single begin curser act as an infinitly small range
|
||||||
|
if (msg_reg.valid(vcb.curser_end) && msg_reg.all_of<Message::Components::ViewCurserEnd>(vcb.curser_end)) {
|
||||||
|
// TODO: respect curser end's begin?
|
||||||
|
// TODO: remember which ends we checked and check remaining
|
||||||
|
ts_end = msg_reg.get<Message::Components::Timestamp>(vcb.curser_end).ts;
|
||||||
|
|
||||||
|
// sanity check curser order
|
||||||
|
if (ts_end > ts_begin) {
|
||||||
|
std::cerr << "MFS warning: begin curser and end curser of view swapped!!\n";
|
||||||
|
std::swap(ts_begin, ts_end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform both checks here
|
||||||
|
if (ts_begin < range_end || ts_end > range_begin) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// range hits a view
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isLess(const std::vector<uint8_t>& lhs, const std::vector<uint8_t>& rhs) {
|
||||||
|
size_t i = 0;
|
||||||
|
for (; i < lhs.size() && i < rhs.size(); i++) {
|
||||||
|
if (lhs[i] < rhs[i]) {
|
||||||
|
return true;
|
||||||
|
} else if (lhs[i] > rhs[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// else continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// here we have equality of common lenths
|
||||||
|
|
||||||
|
// we define smaller arrays to be less
|
||||||
|
return lhs.size() < rhs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
float MessageFragmentStore::tick(float time_delta) {
|
||||||
|
// sync dirty fragments here
|
||||||
|
if (!_fuid_save_queue.empty()) {
|
||||||
|
auto fh = _fs.fragmentHandle(_fuid_save_queue.front().id);
|
||||||
|
auto* reg = _fuid_save_queue.front().reg;
|
||||||
|
assert(reg != nullptr);
|
||||||
|
if (syncFragToStorage(fh, *reg)) {
|
||||||
|
_fuid_save_queue.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load needed fragments here
|
||||||
|
|
||||||
|
// last check event frags
|
||||||
|
// only checks if it collides with ranges, not adjacent
|
||||||
|
// bc ~range~ msgreg will be marked dirty and checked next tick
|
||||||
|
const bool had_events = !_event_check_queue.empty();
|
||||||
|
for (size_t i = 0; i < 10 && !_event_check_queue.empty(); i++) {
|
||||||
|
std::cout << "MFS: event check\n";
|
||||||
|
auto fh = _fs.fragmentHandle(_event_check_queue.front().fid);
|
||||||
|
auto c = _event_check_queue.front().c;
|
||||||
|
_event_check_queue.pop();
|
||||||
|
|
||||||
|
if (!static_cast<bool>(fh)) {
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fh.all_of<FragComp::MessagesTSRange>()) {
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get ts range of frag and collide with all curser(s/ranges)
|
||||||
|
const auto& frag_range = fh.get<FragComp::MessagesTSRange>();
|
||||||
|
|
||||||
|
auto* msg_reg = _rmm.get(c);
|
||||||
|
if (msg_reg == nullptr) {
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rangeVisible(frag_range.begin, frag_range.end, !msg_reg)) {
|
||||||
|
loadFragment(*msg_reg, fh);
|
||||||
|
_potentially_dirty_contacts.emplace(c);
|
||||||
|
return 0.05f; // only one but soon again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (had_events) {
|
||||||
|
std::cout << "MFS: event check none\n";
|
||||||
|
return 0.05f; // only check events, even if non where hit
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_potentially_dirty_contacts.empty()) {
|
||||||
|
std::cout << "MFS: pdc\n";
|
||||||
|
// here we check if any view of said contact needs frag loading
|
||||||
|
// only once per tick tho
|
||||||
|
|
||||||
|
// TODO: this makes order depend on internal order and is not fair
|
||||||
|
auto it = _potentially_dirty_contacts.cbegin();
|
||||||
|
|
||||||
|
auto* msg_reg = _rmm.get(*it);
|
||||||
|
|
||||||
|
// first do collision check agains every contact associated fragment
|
||||||
|
// that is not already loaded !!
|
||||||
|
if (msg_reg->ctx().contains<Message::Components::ContactFragments>()) {
|
||||||
|
const auto& cf = msg_reg->ctx().get<Message::Components::ContactFragments>();
|
||||||
|
if (!cf.frags.empty()) {
|
||||||
|
if (!msg_reg->ctx().contains<Message::Components::LoadedContactFragments>()) {
|
||||||
|
msg_reg->ctx().emplace<Message::Components::LoadedContactFragments>();
|
||||||
|
}
|
||||||
|
const auto& loaded_frags = msg_reg->ctx().get<Message::Components::LoadedContactFragments>().frags;
|
||||||
|
|
||||||
|
for (const auto& [fid, si] : msg_reg->ctx().get<Message::Components::ContactFragments>().frags) {
|
||||||
|
if (loaded_frags.contains(fid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fh = _fs.fragmentHandle(fid);
|
||||||
|
|
||||||
|
if (!static_cast<bool>(fh)) {
|
||||||
|
std::cerr << "MFS error: frag is invalid\n";
|
||||||
|
// WHAT
|
||||||
|
msg_reg->ctx().get<Message::Components::ContactFragments>().erase(fid);
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fh.all_of<FragComp::MessagesTSRange>()) {
|
||||||
|
std::cerr << "MFS error: frag has no range\n";
|
||||||
|
// ????
|
||||||
|
msg_reg->ctx().get<Message::Components::ContactFragments>().erase(fid);
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get ts range of frag and collide with all curser(s/ranges)
|
||||||
|
const auto& [range_begin, range_end] = fh.get<FragComp::MessagesTSRange>();
|
||||||
|
|
||||||
|
if (rangeVisible(range_begin, range_end, *msg_reg)) {
|
||||||
|
std::cout << "MFS: frag hit by vis range\n";
|
||||||
|
loadFragment(*msg_reg, fh);
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no new visible fragment
|
||||||
|
std::cout << "MFS: no new frag directly visible\n";
|
||||||
|
|
||||||
|
// now, finally, check for adjecent fragments that need to be loaded
|
||||||
|
// we do this by finding the outermost fragment in a rage, and extend it by one
|
||||||
|
|
||||||
|
// TODO: rewrite using some bounding range tree to perform collision checks !!!
|
||||||
|
// (this is now performing better, but still)
|
||||||
|
|
||||||
|
|
||||||
|
// for each view
|
||||||
|
auto c_b_view = msg_reg->view<Message::Components::Timestamp, Message::Components::ViewCurserBegin>();
|
||||||
|
c_b_view.use<Message::Components::ViewCurserBegin>();
|
||||||
|
for (const auto& [_, ts_begin_comp, vcb] : c_b_view.each()) {
|
||||||
|
// aka "scroll down"
|
||||||
|
{ // find newest(-ish) frag in range
|
||||||
|
// or in reverse frag end <= range begin
|
||||||
|
|
||||||
|
|
||||||
|
// lower bound of frag end and range begin
|
||||||
|
const auto right = std::lower_bound(
|
||||||
|
cf.sorted_end.crbegin(),
|
||||||
|
cf.sorted_end.crend(),
|
||||||
|
ts_begin_comp.ts,
|
||||||
|
[&](const FragmentID element, const auto& value) -> bool {
|
||||||
|
return _fs._reg.get<FragComp::MessagesTSRange>(element).end >= value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
FragmentID next_frag{entt::null};
|
||||||
|
if (right != cf.sorted_end.crend()) {
|
||||||
|
next_frag = cf.next(*right);
|
||||||
|
}
|
||||||
|
// we checked earlier that cf is not empty
|
||||||
|
if (!_fs._reg.valid(next_frag)) {
|
||||||
|
// fall back to closest, cf is not empty
|
||||||
|
next_frag = cf.sorted_end.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// a single adjacent frag is often not enough
|
||||||
|
// only ok bc next is cheap
|
||||||
|
for (size_t i = 0; i < 5 && _fs._reg.valid(next_frag); i++) {
|
||||||
|
if (!loaded_frags.contains(next_frag)) {
|
||||||
|
std::cout << "MFS: next frag of range\n";
|
||||||
|
loadFragment(*msg_reg, {_fs._reg, next_frag});
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_frag = cf.next(next_frag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// curser end
|
||||||
|
if (!msg_reg->valid(vcb.curser_end) || !msg_reg->all_of<Message::Components::Timestamp>(vcb.curser_end)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto ts_end = msg_reg->get<Message::Components::Timestamp>(vcb.curser_end).ts;
|
||||||
|
|
||||||
|
// aka "scroll up"
|
||||||
|
{ // find oldest(-ish) frag in range
|
||||||
|
// frag begin >= range end
|
||||||
|
|
||||||
|
// lower bound of frag begin and range end
|
||||||
|
const auto left = std::lower_bound(
|
||||||
|
cf.sorted_begin.cbegin(),
|
||||||
|
cf.sorted_begin.cend(),
|
||||||
|
ts_end,
|
||||||
|
[&](const FragmentID element, const auto& value) -> bool {
|
||||||
|
return _fs._reg.get<FragComp::MessagesTSRange>(element).begin < value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
FragmentID prev_frag{entt::null};
|
||||||
|
if (left != cf.sorted_begin.cend()) {
|
||||||
|
prev_frag = cf.prev(*left);
|
||||||
|
}
|
||||||
|
// we checked earlier that cf is not empty
|
||||||
|
if (!_fs._reg.valid(prev_frag)) {
|
||||||
|
// fall back to closest, cf is not empty
|
||||||
|
prev_frag = cf.sorted_begin.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// a single adjacent frag is often not enough
|
||||||
|
// only ok bc next is cheap
|
||||||
|
for (size_t i = 0; i < 5 && _fs._reg.valid(prev_frag); i++) {
|
||||||
|
if (!loaded_frags.contains(prev_frag)) {
|
||||||
|
std::cout << "MFS: prev frag of range\n";
|
||||||
|
loadFragment(*msg_reg, {_fs._reg, prev_frag});
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_frag = cf.prev(prev_frag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// contact has no fragments, skip
|
||||||
|
}
|
||||||
|
|
||||||
|
_potentially_dirty_contacts.erase(it);
|
||||||
|
|
||||||
|
return 0.05f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 1000.f*60.f*60.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageFragmentStore::triggerScan(void) {
|
||||||
|
_fs.scanStoragePath("test_message_store/");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageFragmentStore::onEvent(const Message::Events::MessageConstruct& e) {
|
||||||
|
handleMessage(e.e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageFragmentStore::onEvent(const Message::Events::MessageUpdated& e) {
|
||||||
|
handleMessage(e.e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle deletes? diff between unload?
|
||||||
|
|
||||||
|
bool MessageFragmentStore::onEvent(const Fragment::Events::FragmentConstruct& e) {
|
||||||
|
if (_fs_ignore_event) {
|
||||||
|
return false; // skip self
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.e.all_of<FragComp::MessagesTSRange, FragComp::MessagesContact>()) {
|
||||||
|
return false; // not for us
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: are we sure it is a *new* fragment?
|
||||||
|
|
||||||
|
Contact3 frag_contact = entt::null;
|
||||||
|
{ // get contact
|
||||||
|
const auto& frag_contact_id = e.e.get<FragComp::MessagesContact>().id;
|
||||||
|
// TODO: id lookup table, this is very inefficent
|
||||||
|
for (const auto& [c_it, id_it] : _cr.view<Contact::Components::ID>().each()) {
|
||||||
|
if (frag_contact_id == id_it.data) {
|
||||||
|
frag_contact = c_it;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_cr.valid(frag_contact)) {
|
||||||
|
// unkown contact
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create if not exist
|
||||||
|
auto* msg_reg = _rmm.get(frag_contact);
|
||||||
|
if (msg_reg == nullptr) {
|
||||||
|
// msg reg not created yet
|
||||||
|
// TODO: this is an erroious path
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msg_reg->ctx().contains<Message::Components::ContactFragments>()) {
|
||||||
|
msg_reg->ctx().emplace<Message::Components::ContactFragments>();
|
||||||
|
}
|
||||||
|
msg_reg->ctx().get<Message::Components::ContactFragments>().erase(e.e); // TODO: check/update/fragment update
|
||||||
|
msg_reg->ctx().get<Message::Components::ContactFragments>().insert(e.e);
|
||||||
|
|
||||||
|
_event_check_queue.push(ECQueueEntry{e.e, frag_contact});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Message::Components::ContactFragments::insert(FragmentHandle frag) {
|
||||||
|
if (frags.contains(frag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// both sorted arrays are sorted ascending
|
||||||
|
// so for insertion we search for the last index that is <= and insert after it
|
||||||
|
// or we search for the first > (or end) and insert before it <---
|
||||||
|
// since equal fragments are UB, we can assume they are only > or <
|
||||||
|
|
||||||
|
size_t begin_index {0};
|
||||||
|
{ // begin
|
||||||
|
const auto pos = std::find_if(
|
||||||
|
sorted_begin.cbegin(),
|
||||||
|
sorted_begin.cend(),
|
||||||
|
[frag](const FragmentID a) -> bool {
|
||||||
|
const auto begin_a = frag.registry()->get<FragComp::MessagesTSRange>(a).begin;
|
||||||
|
const auto begin_frag = frag.get<FragComp::MessagesTSRange>().begin;
|
||||||
|
if (begin_a > begin_frag) {
|
||||||
|
return true;
|
||||||
|
} else if (begin_a < begin_frag) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// equal ts, we need to fall back to id (id can not be equal)
|
||||||
|
return isLess(frag.get<FragComp::ID>().v, frag.registry()->get<FragComp::ID>(a).v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
begin_index = std::distance(sorted_begin.cbegin(), pos);
|
||||||
|
|
||||||
|
// we need to insert before pos (end is valid here)
|
||||||
|
sorted_begin.insert(pos, frag);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t end_index {0};
|
||||||
|
{ // end
|
||||||
|
const auto pos = std::find_if_not(
|
||||||
|
sorted_end.cbegin(),
|
||||||
|
sorted_end.cend(),
|
||||||
|
[frag](const FragmentID a) -> bool {
|
||||||
|
const auto end_a = frag.registry()->get<FragComp::MessagesTSRange>(a).end;
|
||||||
|
const auto end_frag = frag.get<FragComp::MessagesTSRange>().end;
|
||||||
|
if (end_a > end_frag) {
|
||||||
|
return true;
|
||||||
|
} else if (end_a < end_frag) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// equal ts, we need to fall back to id (id can not be equal)
|
||||||
|
return isLess(frag.get<FragComp::ID>().v, frag.registry()->get<FragComp::ID>(a).v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
end_index = std::distance(sorted_end.cbegin(), pos);
|
||||||
|
|
||||||
|
// we need to insert before pos (end is valid here)
|
||||||
|
sorted_end.insert(pos, frag);
|
||||||
|
}
|
||||||
|
|
||||||
|
frags.emplace(frag, InternalEntry{begin_index, end_index});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Message::Components::ContactFragments::erase(FragmentID frag) {
|
||||||
|
auto frags_it = frags.find(frag);
|
||||||
|
if (frags_it == frags.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(sorted_begin.size() == sorted_end.size());
|
||||||
|
assert(sorted_begin.size() > frags_it->second.i_b);
|
||||||
|
|
||||||
|
sorted_begin.erase(sorted_begin.begin() + frags_it->second.i_b);
|
||||||
|
sorted_end.erase(sorted_end.begin() + frags_it->second.i_e);
|
||||||
|
|
||||||
|
frags.erase(frags_it);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentID Message::Components::ContactFragments::prev(FragmentID frag) const {
|
||||||
|
// uses range begin to go back in time
|
||||||
|
|
||||||
|
auto it = frags.find(frag);
|
||||||
|
if (it == frags.end()) {
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto src_i = it->second.i_b;
|
||||||
|
if (src_i > 0) {
|
||||||
|
return sorted_begin[src_i-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FragmentID Message::Components::ContactFragments::next(FragmentID frag) const {
|
||||||
|
// uses range end to go forward in time
|
||||||
|
|
||||||
|
auto it = frags.find(frag);
|
||||||
|
if (it == frags.end()) {
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto src_i = it->second.i_e;
|
||||||
|
if (src_i+1 < sorted_end.size()) {
|
||||||
|
return sorted_end[src_i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
132
src/fragment_store/message_fragment_store.hpp
Normal file
132
src/fragment_store/message_fragment_store.hpp
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./meta_components.hpp"
|
||||||
|
#include "./fragment_store_i.hpp"
|
||||||
|
#include "./fragment_store.hpp"
|
||||||
|
|
||||||
|
#include "./message_serializer.hpp"
|
||||||
|
|
||||||
|
#include <entt/entity/registry.hpp>
|
||||||
|
#include <entt/container/dense_map.hpp>
|
||||||
|
#include <entt/container/dense_set.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_model3.hpp>
|
||||||
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Message::Components {
|
||||||
|
|
||||||
|
// unused, consumes too much memory (highly compressable)
|
||||||
|
//using FUID = FragComp::ID;
|
||||||
|
|
||||||
|
struct FID {
|
||||||
|
FragmentID fid {entt::null};
|
||||||
|
};
|
||||||
|
|
||||||
|
// points to the front/newer message
|
||||||
|
// together they define a range that is,
|
||||||
|
// eg the first(end) and last(begin) message being rendered
|
||||||
|
// MFS requires there to be atleast one other fragment after/before,
|
||||||
|
// if not loaded fragment with fitting tsrange(direction) available
|
||||||
|
// uses fragmentAfter/Before()
|
||||||
|
// they can exist standalone
|
||||||
|
// if they are a pair, the inside is filled first
|
||||||
|
// cursers require a timestamp ???
|
||||||
|
struct ViewCurserBegin {
|
||||||
|
Message3 curser_end{entt::null};
|
||||||
|
};
|
||||||
|
struct ViewCurserEnd {
|
||||||
|
Message3 curser_begin{entt::null};
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: add adjacency range comp or inside curser
|
||||||
|
|
||||||
|
// TODO: unused
|
||||||
|
// mfs will only load a limited number of fragments per tick (1),
|
||||||
|
// so this tag will be set if we loaded a fragment and
|
||||||
|
// every tick we check all cursers for this tag and continue
|
||||||
|
// and remove once no fragment could be loaded anymore
|
||||||
|
// (internal)
|
||||||
|
struct TagCurserUnsatisfied {};
|
||||||
|
|
||||||
|
} // Message::Components
|
||||||
|
|
||||||
|
namespace Fragment::Components {
|
||||||
|
struct MessagesTSRange {
|
||||||
|
// timestamp range within the fragment
|
||||||
|
uint64_t begin {0}; // newer msg -> higher number
|
||||||
|
uint64_t end {0};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MessagesContact {
|
||||||
|
std::vector<uint8_t> id;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: add src contact (self id)
|
||||||
|
|
||||||
|
} // Fragment::Components
|
||||||
|
|
||||||
|
// handles fragments for messages
|
||||||
|
// on new message: assign fuid
|
||||||
|
// on new and update: mark as fragment dirty
|
||||||
|
// on delete: mark as fragment dirty?
|
||||||
|
class MessageFragmentStore : public RegistryMessageModelEventI, public FragmentStoreEventI {
|
||||||
|
protected:
|
||||||
|
Contact3Registry& _cr;
|
||||||
|
RegistryMessageModel& _rmm;
|
||||||
|
FragmentStore& _fs;
|
||||||
|
bool _fs_ignore_event {false};
|
||||||
|
|
||||||
|
// for message components only
|
||||||
|
MessageSerializerCallbacks _sc;
|
||||||
|
|
||||||
|
void handleMessage(const Message3Handle& m);
|
||||||
|
|
||||||
|
void loadFragment(Message3Registry& reg, FragmentHandle fh);
|
||||||
|
|
||||||
|
bool syncFragToStorage(FragmentHandle fh, Message3Registry& reg);
|
||||||
|
|
||||||
|
struct SaveQueueEntry final {
|
||||||
|
uint64_t ts_since_dirty{0};
|
||||||
|
//std::vector<uint8_t> id;
|
||||||
|
FragmentID id;
|
||||||
|
Message3Registry* reg{nullptr};
|
||||||
|
};
|
||||||
|
std::queue<SaveQueueEntry> _fuid_save_queue;
|
||||||
|
|
||||||
|
struct ECQueueEntry final {
|
||||||
|
FragmentID fid;
|
||||||
|
Contact3 c;
|
||||||
|
};
|
||||||
|
std::queue<ECQueueEntry> _event_check_queue;
|
||||||
|
|
||||||
|
// range changed or fragment loaded.
|
||||||
|
// we only load a limited number of fragments at once,
|
||||||
|
// so we need to keep them dirty until nothing was loaded.
|
||||||
|
entt::dense_set<Contact3> _potentially_dirty_contacts;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MessageFragmentStore(
|
||||||
|
Contact3Registry& cr,
|
||||||
|
RegistryMessageModel& rmm,
|
||||||
|
FragmentStore& fs
|
||||||
|
);
|
||||||
|
virtual ~MessageFragmentStore(void);
|
||||||
|
|
||||||
|
MessageSerializerCallbacks& getMSC(void);
|
||||||
|
|
||||||
|
float tick(float time_delta);
|
||||||
|
|
||||||
|
void triggerScan(void);
|
||||||
|
|
||||||
|
protected: // rmm
|
||||||
|
bool onEvent(const Message::Events::MessageConstruct& e) override;
|
||||||
|
bool onEvent(const Message::Events::MessageUpdated& e) override;
|
||||||
|
|
||||||
|
protected: // fs
|
||||||
|
bool onEvent(const Fragment::Events::FragmentConstruct& e) override;
|
||||||
|
};
|
||||||
|
|
107
src/fragment_store/message_serializer.cpp
Normal file
107
src/fragment_store/message_serializer.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "./message_serializer.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/message3/components.hpp>
|
||||||
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
static Contact3 findContactByID(Contact3Registry& cr, const std::vector<uint8_t>& id) {
|
||||||
|
// TODO: id lookup table, this is very inefficent
|
||||||
|
for (const auto& [c_it, id_it] : cr.view<Contact::Components::ID>().each()) {
|
||||||
|
if (id == id_it.data) {
|
||||||
|
return c_it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool MessageSerializerCallbacks::component_get_json<Message::Components::ContactFrom>(MessageSerializerCallbacks& msc, const Handle h, nlohmann::json& j) {
|
||||||
|
const Contact3 c = h.get<Message::Components::ContactFrom>().c;
|
||||||
|
if (!msc.cr.valid(c)) {
|
||||||
|
// while this is invalid registry state, it is valid serialization
|
||||||
|
j = nullptr;
|
||||||
|
std::cerr << "MSC warning: encountered invalid contact\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msc.cr.all_of<Contact::Components::ID>(c)) {
|
||||||
|
// unlucky, this contact is purely ephemeral
|
||||||
|
j = nullptr;
|
||||||
|
std::cerr << "MSC warning: encountered contact without ID\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
j = nlohmann::json::binary(msc.cr.get<Contact::Components::ID>(c).data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
bool MessageSerializerCallbacks::component_emplace_or_replace_json<Message::Components::ContactFrom>(MessageSerializerCallbacks& msc, Handle h, const nlohmann::json& j) {
|
||||||
|
if (j.is_null()) {
|
||||||
|
std::cerr << "MSC warning: encountered null contact\n";
|
||||||
|
h.emplace_or_replace<Message::Components::ContactFrom>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<uint8_t> id = j.is_binary()?j:j["bytes"];
|
||||||
|
|
||||||
|
Contact3 other_c = findContactByID(msc.cr, id);
|
||||||
|
if (!msc.cr.valid(other_c)) {
|
||||||
|
// create sparse contact with id only
|
||||||
|
other_c = msc.cr.create();
|
||||||
|
msc.cr.emplace_or_replace<Contact::Components::ID>(other_c, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
h.emplace_or_replace<Message::Components::ContactFrom>(other_c);
|
||||||
|
|
||||||
|
// TODO: should we return false if the contact is unknown??
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
bool MessageSerializerCallbacks::component_get_json<Message::Components::ContactTo>(MessageSerializerCallbacks& msc, const Handle h, nlohmann::json& j) {
|
||||||
|
const Contact3 c = h.get<Message::Components::ContactTo>().c;
|
||||||
|
if (!msc.cr.valid(c)) {
|
||||||
|
// while this is invalid registry state, it is valid serialization
|
||||||
|
j = nullptr;
|
||||||
|
std::cerr << "MSC warning: encountered invalid contact\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msc.cr.all_of<Contact::Components::ID>(c)) {
|
||||||
|
// unlucky, this contact is purely ephemeral
|
||||||
|
j = nullptr;
|
||||||
|
std::cerr << "MSC warning: encountered contact without ID\n";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
j = nlohmann::json::binary(msc.cr.get<Contact::Components::ID>(c).data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
bool MessageSerializerCallbacks::component_emplace_or_replace_json<Message::Components::ContactTo>(MessageSerializerCallbacks& msc, Handle h, const nlohmann::json& j) {
|
||||||
|
if (j.is_null()) {
|
||||||
|
std::cerr << "MSC warning: encountered null contact\n";
|
||||||
|
h.emplace_or_replace<Message::Components::ContactTo>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<uint8_t> id = j.is_binary()?j:j["bytes"];
|
||||||
|
|
||||||
|
Contact3 other_c = findContactByID(msc.cr, id);
|
||||||
|
if (!msc.cr.valid(other_c)) {
|
||||||
|
// create sparse contact with id only
|
||||||
|
other_c = msc.cr.create();
|
||||||
|
msc.cr.emplace_or_replace<Contact::Components::ID>(other_c, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
h.emplace_or_replace<Message::Components::ContactTo>(other_c);
|
||||||
|
|
||||||
|
// TODO: should we return false if the contact is unknown??
|
||||||
|
return true;
|
||||||
|
}
|
85
src/fragment_store/message_serializer.hpp
Normal file
85
src/fragment_store/message_serializer.hpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <entt/core/type_info.hpp>
|
||||||
|
#include <entt/container/dense_map.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
|
||||||
|
struct MessageSerializerCallbacks {
|
||||||
|
using Registry = Message3Registry;
|
||||||
|
using Handle = Message3Handle;
|
||||||
|
|
||||||
|
Contact3Registry& cr;
|
||||||
|
|
||||||
|
// nlohmann
|
||||||
|
// json/msgpack
|
||||||
|
using serialize_json_fn = bool(*)(MessageSerializerCallbacks& msc, const Handle h, nlohmann::json& out);
|
||||||
|
entt::dense_map<entt::id_type, serialize_json_fn> _serl_json;
|
||||||
|
|
||||||
|
using deserialize_json_fn = bool(*)(MessageSerializerCallbacks& msc, Handle h, const nlohmann::json& in);
|
||||||
|
entt::dense_map<entt::id_type, deserialize_json_fn> _deserl_json;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static bool component_get_json(MessageSerializerCallbacks&, const Handle h, nlohmann::json& j) {
|
||||||
|
if (h.template all_of<T>()) {
|
||||||
|
if constexpr (!std::is_empty_v<T>) {
|
||||||
|
j = h.template get<T>();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static bool component_emplace_or_replace_json(MessageSerializerCallbacks&, Handle h, const nlohmann::json& j) {
|
||||||
|
if constexpr (std::is_empty_v<T>) {
|
||||||
|
h.template emplace_or_replace<T>(); // assert empty json?
|
||||||
|
} else {
|
||||||
|
h.template emplace_or_replace<T>(static_cast<T>(j));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerSerializerJson(serialize_json_fn fn, const entt::type_info& type_info) {
|
||||||
|
_serl_json[type_info.hash()] = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CompType>
|
||||||
|
void registerSerializerJson(
|
||||||
|
serialize_json_fn fn = component_get_json<CompType>,
|
||||||
|
const entt::type_info& type_info = entt::type_id<CompType>()
|
||||||
|
) {
|
||||||
|
registerSerializerJson(fn, type_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerDeSerializerJson(deserialize_json_fn fn, const entt::type_info& type_info) {
|
||||||
|
_deserl_json[type_info.hash()] = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CompType>
|
||||||
|
void registerDeSerializerJson(
|
||||||
|
deserialize_json_fn fn = component_emplace_or_replace_json<CompType>,
|
||||||
|
const entt::type_info& type_info = entt::type_id<CompType>()
|
||||||
|
) {
|
||||||
|
registerDeSerializerJson(fn, type_info);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// fwd
|
||||||
|
namespace Message::Components {
|
||||||
|
struct ContactFrom;
|
||||||
|
struct ContactTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make specializations known
|
||||||
|
template<>
|
||||||
|
bool MessageSerializerCallbacks::component_get_json<Message::Components::ContactFrom>(MessageSerializerCallbacks& msc, const Handle h, nlohmann::json& j);
|
||||||
|
template<>
|
||||||
|
bool MessageSerializerCallbacks::component_emplace_or_replace_json<Message::Components::ContactFrom>(MessageSerializerCallbacks& msc, Handle h, const nlohmann::json& j);
|
||||||
|
template<>
|
||||||
|
bool MessageSerializerCallbacks::component_get_json<Message::Components::ContactTo>(MessageSerializerCallbacks& msc, const Handle h, nlohmann::json& j);
|
||||||
|
template<>
|
||||||
|
bool MessageSerializerCallbacks::component_emplace_or_replace_json<Message::Components::ContactTo>(MessageSerializerCallbacks& msc, Handle h, const nlohmann::json& j);
|
60
src/fragment_store/meta_components.hpp
Normal file
60
src/fragment_store/meta_components.hpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./types.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Fragment::Components {
|
||||||
|
|
||||||
|
// TODO: is this special and should this be saved to meta or not (its already in the file name on disk)
|
||||||
|
struct ID {
|
||||||
|
std::vector<uint8_t> v;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DataEncryptionType {
|
||||||
|
Encryption enc {Encryption::NONE};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DataCompressionType {
|
||||||
|
Compression comp {Compression::NONE};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// meta that is not written to (meta-)file
|
||||||
|
namespace Ephemeral {
|
||||||
|
|
||||||
|
// excluded from file meta
|
||||||
|
struct FilePath {
|
||||||
|
// contains store path, if any
|
||||||
|
std::string path;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: seperate into remote and local?
|
||||||
|
// (remote meaning eg. the file on disk was changed by another program)
|
||||||
|
struct DirtyTag {};
|
||||||
|
|
||||||
|
|
||||||
|
// type as comp
|
||||||
|
struct MetaFileType {
|
||||||
|
::MetaFileType type {::MetaFileType::TEXT_JSON};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MetaEncryptionType {
|
||||||
|
Encryption enc {Encryption::NONE};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MetaCompressionType {
|
||||||
|
Compression comp {Compression::NONE};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Ephemeral
|
||||||
|
|
||||||
|
} // Components
|
||||||
|
|
||||||
|
// shortened to save bytes (until I find a way to save by ID in msgpack)
|
||||||
|
namespace FragComp = Fragment::Components;
|
||||||
|
|
||||||
|
#include "./meta_components_id.inl"
|
||||||
|
|
26
src/fragment_store/meta_components_id.inl
Normal file
26
src/fragment_store/meta_components_id.inl
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./meta_components.hpp"
|
||||||
|
|
||||||
|
#include <entt/core/type_info.hpp>
|
||||||
|
|
||||||
|
// TODO: move more central
|
||||||
|
#define DEFINE_COMP_ID(x) \
|
||||||
|
template<> \
|
||||||
|
constexpr entt::id_type entt::type_hash<x>::value() noexcept { \
|
||||||
|
using namespace entt::literals; \
|
||||||
|
return #x##_hs; \
|
||||||
|
} \
|
||||||
|
template<> \
|
||||||
|
constexpr std::string_view entt::type_name<x>::value() noexcept { \
|
||||||
|
return #x; \
|
||||||
|
}
|
||||||
|
|
||||||
|
// cross compiler stable ids
|
||||||
|
|
||||||
|
DEFINE_COMP_ID(FragComp::DataEncryptionType)
|
||||||
|
DEFINE_COMP_ID(FragComp::DataCompressionType)
|
||||||
|
|
||||||
|
#undef DEFINE_COMP_ID
|
||||||
|
|
||||||
|
|
35
src/fragment_store/register_mfs_json_message_components.cpp
Normal file
35
src/fragment_store/register_mfs_json_message_components.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include "./register_mfs_json_message_components.hpp"
|
||||||
|
|
||||||
|
#include "./message_serializer.hpp"
|
||||||
|
#include "../json/message_components.hpp"
|
||||||
|
|
||||||
|
void registerMFSJsonMessageComponents(MessageSerializerCallbacks& msc) {
|
||||||
|
msc.registerSerializerJson<Message::Components::Timestamp>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::Timestamp>();
|
||||||
|
msc.registerSerializerJson<Message::Components::TimestampProcessed>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::TimestampProcessed>();
|
||||||
|
msc.registerSerializerJson<Message::Components::TimestampWritten>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::TimestampWritten>();
|
||||||
|
msc.registerSerializerJson<Message::Components::ContactFrom>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::ContactFrom>();
|
||||||
|
msc.registerSerializerJson<Message::Components::ContactTo>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::ContactTo>();
|
||||||
|
msc.registerSerializerJson<Message::Components::TagUnread>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::TagUnread>();
|
||||||
|
msc.registerSerializerJson<Message::Components::Read>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::Read>();
|
||||||
|
msc.registerSerializerJson<Message::Components::MessageText>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::MessageText>();
|
||||||
|
msc.registerSerializerJson<Message::Components::TagMessageIsAction>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::TagMessageIsAction>();
|
||||||
|
|
||||||
|
// files
|
||||||
|
//_sc.registerSerializerJson<Message::Components::Transfer::FileID>()
|
||||||
|
//_sc.registerSerializerJson<Message::Components::Transfer::FileInfo>();
|
||||||
|
//_sc.registerDeSerializerJson<Message::Components::Transfer::FileInfo>();
|
||||||
|
//_sc.registerSerializerJson<Message::Components::Transfer::FileInfoLocal>();
|
||||||
|
//_sc.registerDeSerializerJson<Message::Components::Transfer::FileInfoLocal>();
|
||||||
|
//_sc.registerSerializerJson<Message::Components::Transfer::TagHaveAll>();
|
||||||
|
//_sc.registerDeSerializerJson<Message::Components::Transfer::TagHaveAll>();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./message_serializer.hpp"
|
||||||
|
|
||||||
|
void registerMFSJsonMessageComponents(MessageSerializerCallbacks& msc);
|
||||||
|
|
@ -0,0 +1,10 @@
|
|||||||
|
#include "./register_mfs_json_message_components.hpp"
|
||||||
|
|
||||||
|
#include "./message_serializer.hpp"
|
||||||
|
#include "../json/tox_message_components.hpp"
|
||||||
|
|
||||||
|
void registerMFSJsonToxMessageComponents(MessageSerializerCallbacks& msc) {
|
||||||
|
msc.registerSerializerJson<Message::Components::ToxGroupMessageID>();
|
||||||
|
msc.registerDeSerializerJson<Message::Components::ToxGroupMessageID>();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./message_serializer.hpp"
|
||||||
|
|
||||||
|
void registerMFSJsonToxMessageComponents(MessageSerializerCallbacks& msc);
|
||||||
|
|
68
src/fragment_store/serializer.hpp
Normal file
68
src/fragment_store/serializer.hpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <entt/core/type_info.hpp>
|
||||||
|
#include <entt/container/dense_map.hpp>
|
||||||
|
#include <entt/entity/handle.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
|
||||||
|
template<typename EntityType = entt::entity>
|
||||||
|
struct SerializerCallbacks {
|
||||||
|
using Registry = entt::basic_registry<EntityType>;
|
||||||
|
using Handle = entt::basic_handle<Registry>;
|
||||||
|
|
||||||
|
// nlohmann
|
||||||
|
// json/msgpack
|
||||||
|
using serialize_json_fn = bool(*)(const Handle h, nlohmann::json& out);
|
||||||
|
entt::dense_map<entt::id_type, serialize_json_fn> _serl_json;
|
||||||
|
|
||||||
|
using deserialize_json_fn = bool(*)(Handle h, const nlohmann::json& in);
|
||||||
|
entt::dense_map<entt::id_type, deserialize_json_fn> _deserl_json;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static bool component_get_json(const Handle h, nlohmann::json& j) {
|
||||||
|
if (h.template all_of<T>()) {
|
||||||
|
if constexpr (!std::is_empty_v<T>) {
|
||||||
|
j = h.template get<T>();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static bool component_emplace_or_replace_json(Handle h, const nlohmann::json& j) {
|
||||||
|
if constexpr (std::is_empty_v<T>) {
|
||||||
|
h.template emplace_or_replace<T>(); // assert empty json?
|
||||||
|
} else {
|
||||||
|
h.template emplace_or_replace<T>(static_cast<T>(j));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerSerializerJson(serialize_json_fn fn, const entt::type_info& type_info) {
|
||||||
|
_serl_json[type_info.hash()] = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CompType>
|
||||||
|
void registerSerializerJson(
|
||||||
|
serialize_json_fn fn = component_get_json<CompType>,
|
||||||
|
const entt::type_info& type_info = entt::type_id<CompType>()
|
||||||
|
) {
|
||||||
|
registerSerializerJson(fn, type_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerDeSerializerJson(deserialize_json_fn fn, const entt::type_info& type_info) {
|
||||||
|
_deserl_json[type_info.hash()] = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CompType>
|
||||||
|
void registerDeSerializerJson(
|
||||||
|
deserialize_json_fn fn = component_emplace_or_replace_json<CompType>,
|
||||||
|
const entt::type_info& type_info = entt::type_id<CompType>()
|
||||||
|
) {
|
||||||
|
registerDeSerializerJson(fn, type_info);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
80
src/fragment_store/test_fragstore.cpp
Normal file
80
src/fragment_store/test_fragstore.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "./fragment_store.hpp"
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <entt/entity/handle.hpp>
|
||||||
|
|
||||||
|
namespace Components {
|
||||||
|
struct MessagesTimestampRange {
|
||||||
|
uint64_t begin {0};
|
||||||
|
uint64_t end {1000};
|
||||||
|
};
|
||||||
|
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MessagesTimestampRange, begin, end)
|
||||||
|
} // Components
|
||||||
|
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
FragmentStore fs;
|
||||||
|
fs._default_store_path = "test_store/";
|
||||||
|
fs._sc.registerSerializerJson<Components::MessagesTimestampRange>();
|
||||||
|
fs._sc.registerDeSerializerJson<Components::MessagesTimestampRange>();
|
||||||
|
|
||||||
|
const auto frag0 = fs.newFragmentFile("", MetaFileType::TEXT_JSON, {0xff, 0xf1, 0xf2, 0xf0, 0xff, 0xff, 0xff, 0xf9});
|
||||||
|
|
||||||
|
const auto frag1 = fs.newFragmentFile("", MetaFileType::BINARY_MSGPACK);
|
||||||
|
|
||||||
|
const auto frag2 = fs.newFragmentFile("", MetaFileType::BINARY_MSGPACK);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto frag0h = fs.fragmentHandle(frag0);
|
||||||
|
|
||||||
|
frag0h.emplace_or_replace<FragComp::DataCompressionType>();
|
||||||
|
frag0h.emplace_or_replace<FragComp::DataEncryptionType>();
|
||||||
|
frag0h.emplace_or_replace<Components::MessagesTimestampRange>();
|
||||||
|
|
||||||
|
std::function<FragmentStore::write_to_storage_fetch_data_cb> fn_cb = [read = 0ul](uint8_t* request_buffer, uint64_t buffer_size) mutable -> uint64_t {
|
||||||
|
uint64_t i = 0;
|
||||||
|
for (; i+read < 3000 && i < buffer_size; i++) {
|
||||||
|
request_buffer[i] = uint8_t((i+read) & 0xff);
|
||||||
|
}
|
||||||
|
read += i;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
fs.syncToStorage(frag0, fn_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto frag1h = fs.fragmentHandle(frag1);
|
||||||
|
|
||||||
|
frag1h.emplace_or_replace<FragComp::DataCompressionType>();
|
||||||
|
frag1h.emplace_or_replace<FragComp::DataEncryptionType>();
|
||||||
|
|
||||||
|
std::function<FragmentStore::write_to_storage_fetch_data_cb> fn_cb = [read = 0ul](uint8_t* request_buffer, uint64_t buffer_size) mutable -> uint64_t {
|
||||||
|
static constexpr std::string_view text = "This is some random data";
|
||||||
|
uint64_t i = 0;
|
||||||
|
for (; i+read < text.size() && i < buffer_size; i++) {
|
||||||
|
request_buffer[i] = text[i+read];
|
||||||
|
}
|
||||||
|
read += i;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
fs.syncToStorage(frag1, fn_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto frag2h = fs.fragmentHandle(frag2);
|
||||||
|
|
||||||
|
frag2h.emplace_or_replace<FragComp::DataCompressionType>();
|
||||||
|
frag2h.emplace_or_replace<FragComp::DataEncryptionType>();
|
||||||
|
|
||||||
|
static constexpr std::string_view text = "This is more random data";
|
||||||
|
fs.syncToStorage(frag2, reinterpret_cast<const uint8_t*>(text.data()), text.size());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
19
src/fragment_store/types.hpp
Normal file
19
src/fragment_store/types.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
enum class Encryption : uint8_t {
|
||||||
|
NONE = 0x00,
|
||||||
|
};
|
||||||
|
enum class Compression : uint8_t {
|
||||||
|
NONE = 0x00,
|
||||||
|
ZSTD = 0x01,
|
||||||
|
// TODO: zstd without magic
|
||||||
|
// TODO: zstd meta dict
|
||||||
|
// TODO: zstd data(message) dict
|
||||||
|
};
|
||||||
|
enum class MetaFileType : uint8_t {
|
||||||
|
TEXT_JSON,
|
||||||
|
BINARY_MSGPACK, // msgpacked msgpack
|
||||||
|
};
|
||||||
|
|
27
src/json/message_components.hpp
Normal file
27
src/json/message_components.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/util/utils.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/message3/components.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace Message::Components {
|
||||||
|
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Timestamp, ts)
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(TimestampProcessed, ts)
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(TimestampWritten, ts)
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ContactFrom, c)
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ContactTo, c)
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Read, ts)
|
||||||
|
// TODO: SyncedBy
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(MessageText, text)
|
||||||
|
|
||||||
|
namespace Transfer {
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FileInfo::FileDirEntry, file_name, file_size)
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FileInfo, file_list, total_size)
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FileInfoLocal, file_list)
|
||||||
|
} // Transfer
|
||||||
|
|
||||||
|
} // Message::Components
|
||||||
|
|
16
src/json/tox_message_components.hpp
Normal file
16
src/json/tox_message_components.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/util/utils.hpp>
|
||||||
|
|
||||||
|
#include <solanaceae/tox_messages/components.hpp>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace Message::Components {
|
||||||
|
|
||||||
|
// TODO: friend msg id, does not have the same qualities
|
||||||
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ToxGroupMessageID, id)
|
||||||
|
// TODO: transfer stuff, needs content rewrite
|
||||||
|
|
||||||
|
} // Message::Components
|
||||||
|
|
@ -1,5 +1,8 @@
|
|||||||
#include "./main_screen.hpp"
|
#include "./main_screen.hpp"
|
||||||
|
|
||||||
|
#include "./fragment_store/register_mfs_json_message_components.hpp"
|
||||||
|
#include "./fragment_store/register_mfs_json_tox_message_components.hpp"
|
||||||
|
|
||||||
#include <solanaceae/contact/components.hpp>
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
@ -13,6 +16,7 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
|
|||||||
renderer(renderer_),
|
renderer(renderer_),
|
||||||
rmm(cr),
|
rmm(cr),
|
||||||
mts(rmm),
|
mts(rmm),
|
||||||
|
mfs(cr, rmm, fs),
|
||||||
tc(save_path, save_password),
|
tc(save_path, save_password),
|
||||||
tpi(tc.getTox()),
|
tpi(tc.getTox()),
|
||||||
ad(tc),
|
ad(tc),
|
||||||
@ -33,6 +37,8 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
|
|||||||
tdch(tpi)
|
tdch(tpi)
|
||||||
{
|
{
|
||||||
tel.subscribeAll(tc);
|
tel.subscribeAll(tc);
|
||||||
|
registerMFSJsonMessageComponents(mfs.getMSC());
|
||||||
|
registerMFSJsonToxMessageComponents(mfs.getMSC());
|
||||||
|
|
||||||
conf.set("tox", "save_file_path", save_path);
|
conf.set("tox", "save_file_path", save_path);
|
||||||
|
|
||||||
@ -49,6 +55,10 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
|
|||||||
std::cout << "own address: " << tc.toxSelfGetAddressStr() << "\n";
|
std::cout << "own address: " << tc.toxSelfGetAddressStr() << "\n";
|
||||||
|
|
||||||
{ // setup plugin instances
|
{ // setup plugin instances
|
||||||
|
// TODO: make interface useful
|
||||||
|
g_provideInstance<FragmentStoreI>("FragmentStoreI", "host", &fs);
|
||||||
|
g_provideInstance<FragmentStore>("FragmentStore", "host", &fs);
|
||||||
|
|
||||||
g_provideInstance<ConfigModelI>("ConfigModelI", "host", &conf);
|
g_provideInstance<ConfigModelI>("ConfigModelI", "host", &conf);
|
||||||
g_provideInstance<Contact3Registry>("Contact3Registry", "1", "host", &cr);
|
g_provideInstance<Contact3Registry>("Contact3Registry", "1", "host", &cr);
|
||||||
g_provideInstance<RegistryMessageModel>("RegistryMessageModel", "host", &rmm);
|
g_provideInstance<RegistryMessageModel>("RegistryMessageModel", "host", &rmm);
|
||||||
@ -74,6 +84,8 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
conf.dump();
|
conf.dump();
|
||||||
|
|
||||||
|
mfs.triggerScan(); // HACK: after plugins and tox contacts got loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
MainScreen::~MainScreen(void) {
|
MainScreen::~MainScreen(void) {
|
||||||
@ -388,7 +400,8 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
|
|||||||
|
|
||||||
tdch.tick(time_delta); // compute
|
tdch.tick(time_delta); // compute
|
||||||
|
|
||||||
mts.iterate(); // compute
|
const float mfs_interval = mfs.tick(time_delta);
|
||||||
|
mts.iterate(); // compute (after mfs)
|
||||||
|
|
||||||
_min_tick_interval = std::min<float>(
|
_min_tick_interval = std::min<float>(
|
||||||
// HACK: pow by 1.6 to increase 50 -> ~500 (~522)
|
// HACK: pow by 1.6 to increase 50 -> ~500 (~522)
|
||||||
@ -400,6 +413,10 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
|
|||||||
_min_tick_interval,
|
_min_tick_interval,
|
||||||
fo_interval
|
fo_interval
|
||||||
);
|
);
|
||||||
|
_min_tick_interval = std::min<float>(
|
||||||
|
_min_tick_interval,
|
||||||
|
mfs_interval
|
||||||
|
);
|
||||||
|
|
||||||
//std::cout << "MS: min tick interval: " << _min_tick_interval << "\n";
|
//std::cout << "MS: min tick interval: " << _min_tick_interval << "\n";
|
||||||
|
|
||||||
|
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
#include "./screen.hpp"
|
#include "./screen.hpp"
|
||||||
|
|
||||||
|
#include "./fragment_store/fragment_store.hpp"
|
||||||
#include <solanaceae/util/simple_config_model.hpp>
|
#include <solanaceae/util/simple_config_model.hpp>
|
||||||
#include <solanaceae/contact/contact_model3.hpp>
|
#include <solanaceae/contact/contact_model3.hpp>
|
||||||
#include <solanaceae/message3/registry_message_model.hpp>
|
#include <solanaceae/message3/registry_message_model.hpp>
|
||||||
#include <solanaceae/message3/message_time_sort.hpp>
|
#include <solanaceae/message3/message_time_sort.hpp>
|
||||||
|
#include "./fragment_store/message_fragment_store.hpp"
|
||||||
#include <solanaceae/plugin/plugin_manager.hpp>
|
#include <solanaceae/plugin/plugin_manager.hpp>
|
||||||
#include <solanaceae/toxcore/tox_event_logger.hpp>
|
#include <solanaceae/toxcore/tox_event_logger.hpp>
|
||||||
#include "./tox_private_impl.hpp"
|
#include "./tox_private_impl.hpp"
|
||||||
@ -43,10 +45,13 @@ extern "C" {
|
|||||||
struct MainScreen final : public Screen {
|
struct MainScreen final : public Screen {
|
||||||
SDL_Renderer* renderer;
|
SDL_Renderer* renderer;
|
||||||
|
|
||||||
|
FragmentStore fs;
|
||||||
|
|
||||||
SimpleConfigModel conf;
|
SimpleConfigModel conf;
|
||||||
Contact3Registry cr;
|
Contact3Registry cr;
|
||||||
RegistryMessageModel rmm;
|
RegistryMessageModel rmm;
|
||||||
MessageTimeSort mts;
|
MessageTimeSort mts;
|
||||||
|
MessageFragmentStore mfs;
|
||||||
|
|
||||||
PluginManager pm;
|
PluginManager pm;
|
||||||
|
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
#include "./sdlrenderer_texture_uploader.hpp"
|
#include "./sdlrenderer_texture_uploader.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
SDLRendererTextureUploader::SDLRendererTextureUploader(SDL_Renderer* renderer_) :
|
SDLRendererTextureUploader::SDLRendererTextureUploader(SDL_Renderer* renderer_) :
|
||||||
renderer(renderer_)
|
renderer(renderer_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t SDLRendererTextureUploader::uploadRGBA(const uint8_t* data, uint32_t width, uint32_t height, Filter filter) {
|
uint64_t SDLRendererTextureUploader::uploadRGBA(const uint8_t* data, uint32_t width, uint32_t height, Filter filter, Access access) {
|
||||||
// TODO: test if pitch is 4 or 4*width
|
// TODO: test if pitch is 4 or 4*width
|
||||||
SDL_Surface* surf = SDL_CreateSurfaceFrom(
|
SDL_Surface* surf = SDL_CreateSurfaceFrom(
|
||||||
(void*)data,
|
(void*)data,
|
||||||
@ -17,8 +21,15 @@ uint64_t SDLRendererTextureUploader::uploadRGBA(const uint8_t* data, uint32_t wi
|
|||||||
);
|
);
|
||||||
assert(surf); // TODO: add error reporting
|
assert(surf); // TODO: add error reporting
|
||||||
|
|
||||||
SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
|
SDL_Texture* tex = SDL_CreateTexture(
|
||||||
|
renderer,
|
||||||
|
surf->format->format,
|
||||||
|
access == Access::STREAMING ? SDL_TEXTUREACCESS_STREAMING : SDL_TEXTUREACCESS_STATIC,
|
||||||
|
surf->w, surf->h
|
||||||
|
);
|
||||||
assert(tex); // TODO: add error reporting
|
assert(tex); // TODO: add error reporting
|
||||||
|
// TODO: error reporting
|
||||||
|
SDL_UpdateTexture(tex, nullptr, surf->pixels, surf->pitch);
|
||||||
|
|
||||||
if (filter == NEAREST) {
|
if (filter == NEAREST) {
|
||||||
SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
|
SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
|
||||||
@ -31,6 +42,27 @@ uint64_t SDLRendererTextureUploader::uploadRGBA(const uint8_t* data, uint32_t wi
|
|||||||
return reinterpret_cast<uint64_t>(tex);
|
return reinterpret_cast<uint64_t>(tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SDLRendererTextureUploader::updateRGBA(uint64_t tex_id, const uint8_t* data, size_t size) {
|
||||||
|
auto* texture = static_cast<SDL_Texture*>(reinterpret_cast<void*>(tex_id));
|
||||||
|
if (texture == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* pixels = nullptr;
|
||||||
|
int pitch = 0;
|
||||||
|
|
||||||
|
if (SDL_LockTexture(texture, nullptr, (void**)&pixels, &pitch) != 0) {
|
||||||
|
std::cerr << "SDLRTU error: failed locking texture '" << SDL_GetError() << "'\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(pixels, data, size);
|
||||||
|
|
||||||
|
SDL_UnlockTexture(texture);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void SDLRendererTextureUploader::destroy(uint64_t tex_id) {
|
void SDLRendererTextureUploader::destroy(uint64_t tex_id) {
|
||||||
SDL_DestroyTexture(static_cast<SDL_Texture*>(reinterpret_cast<void*>(tex_id)));
|
SDL_DestroyTexture(static_cast<SDL_Texture*>(reinterpret_cast<void*>(tex_id)));
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,8 @@ struct SDLRendererTextureUploader : public TextureUploaderI {
|
|||||||
SDLRendererTextureUploader(SDL_Renderer* renderer_);
|
SDLRendererTextureUploader(SDL_Renderer* renderer_);
|
||||||
~SDLRendererTextureUploader(void) = default;
|
~SDLRendererTextureUploader(void) = default;
|
||||||
|
|
||||||
uint64_t uploadRGBA(const uint8_t* data, uint32_t width, uint32_t height, Filter filter) override;
|
uint64_t uploadRGBA(const uint8_t* data, uint32_t width, uint32_t height, Filter filter, Access access) override;
|
||||||
|
bool updateRGBA(uint64_t tex_id, const uint8_t* data, size_t size) override;
|
||||||
void destroy(uint64_t tex_id) override;
|
void destroy(uint64_t tex_id) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
struct TextureUploaderI {
|
struct TextureUploaderI {
|
||||||
static constexpr const char* version {"1"};
|
static constexpr const char* version {"2"};
|
||||||
|
|
||||||
enum Filter {
|
enum Filter {
|
||||||
NEAREST,
|
NEAREST,
|
||||||
LINEAR,
|
LINEAR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Access {
|
||||||
|
STATIC,
|
||||||
|
STREAMING,
|
||||||
|
// target?
|
||||||
|
};
|
||||||
|
|
||||||
virtual ~TextureUploaderI(void) {}
|
virtual ~TextureUploaderI(void) {}
|
||||||
|
|
||||||
virtual uint64_t uploadRGBA(const uint8_t* data, uint32_t width, uint32_t height, Filter filter = LINEAR) = 0;
|
virtual uint64_t uploadRGBA(const uint8_t* data, uint32_t width, uint32_t height, Filter filter = LINEAR, Access access = STATIC) = 0;
|
||||||
|
|
||||||
|
// keeps width height filter
|
||||||
|
// TODO: wh instead of size?
|
||||||
|
virtual bool updateRGBA(uint64_t tex_id, const uint8_t* data, size_t size) = 0;
|
||||||
|
|
||||||
virtual void destroy(uint64_t tex_id) = 0;
|
virtual void destroy(uint64_t tex_id) = 0;
|
||||||
};
|
};
|
||||||
|
@ -120,7 +120,8 @@ ToxFriendFauxOfflineMessaging::dfmc_Ret ToxFriendFauxOfflineMessaging::doFriendM
|
|||||||
// require
|
// require
|
||||||
if (!mr->all_of<
|
if (!mr->all_of<
|
||||||
Message::Components::MessageText, // text only for now
|
Message::Components::MessageText, // text only for now
|
||||||
Message::Components::ContactTo
|
Message::Components::ContactTo,
|
||||||
|
Message::Components::ToxFriendMessageID // yes, needs fake ids
|
||||||
>(msg)
|
>(msg)
|
||||||
) {
|
) {
|
||||||
continue; // skip
|
continue; // skip
|
||||||
|
@ -16,4 +16,20 @@ struct ToxPrivateImpl : public ToxPrivateI {
|
|||||||
uint16_t toxDHTGetNumCloselistAnnounceCapable(void) override {
|
uint16_t toxDHTGetNumCloselistAnnounceCapable(void) override {
|
||||||
return tox_dht_get_num_closelist_announce_capable(_tox);
|
return tox_dht_get_num_closelist_announce_capable(_tox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::tuple<std::optional<std::string>, Tox_Err_Group_Peer_Query> toxGroupPeerGetIPAddress(uint32_t group_number, uint32_t peer_id) override {
|
||||||
|
Tox_Err_Group_Peer_Query err = TOX_ERR_GROUP_PEER_QUERY_OK;
|
||||||
|
size_t str_size = tox_group_peer_get_ip_address_size(_tox, group_number, peer_id, &err);
|
||||||
|
if (err != TOX_ERR_GROUP_PEER_QUERY_OK) {
|
||||||
|
return {std::nullopt, err};
|
||||||
|
}
|
||||||
|
std::string ip_str(str_size, '\0');
|
||||||
|
|
||||||
|
tox_group_peer_get_ip_address(_tox, group_number, peer_id, reinterpret_cast<uint8_t*>(ip_str.data()), &err);
|
||||||
|
if (err == TOX_ERR_GROUP_PEER_QUERY_OK) {
|
||||||
|
return {ip_str, err};
|
||||||
|
} else {
|
||||||
|
return {std::nullopt, err};
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user