diff --git a/sc-memory/sc-core/CMakeLists.txt b/sc-memory/sc-core/CMakeLists.txt index 7109b9953..9867b3ef9 100644 --- a/sc-memory/sc-core/CMakeLists.txt +++ b/sc-memory/sc-core/CMakeLists.txt @@ -8,7 +8,7 @@ file(GLOB SOURCES CONFIGURE_DEPENDS find_glib() add_library(sc-core SHARED ${SOURCES}) -target_link_libraries(sc-core LINK_PRIVATE ${glib_LIBRARIES}) +target_link_libraries(sc-core LINK_PRIVATE ${glib_LIBRARIES} aio ) target_include_directories(sc-core PRIVATE ${glib_INCLUDE_DIRS} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src diff --git a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_dictionary_fs_memory.c b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_dictionary_fs_memory.c index 1b6278914..92d6e1cbc 100644 --- a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_dictionary_fs_memory.c +++ b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_dictionary_fs_memory.c @@ -22,12 +22,93 @@ # define DEFAULT_STRING_INT_SIZE 20 # define DEFAULT_MAX_SEARCHABLE_STRING_SIZE 1000 +#define BUFFER_SIZE_LIMIT (10 * 1024 * 1024) // 10 МБ + + typedef struct { sc_list * link_hashes; sc_uint64 string_offset; } sc_link_hash_content; + + +void _sc_dictionary_fs_memory_buffer_init(sc_write_buffer ** buffer, sc_uint64 capacity) +{ + *buffer = sc_mem_new(sc_write_buffer, 1); + (*buffer)->data = sc_mem_new(sc_char, capacity); + (*buffer)->size = 0; + (*buffer)->capacity = capacity; + sc_monitor_init(&(*buffer)->buffer_monitor); +} + +// Уничтожение буфера +void _sc_dictionary_fs_memory_buffer_destroy(sc_write_buffer * buffer) +{ + if (buffer == null_ptr) + return; + sc_mem_free(buffer->data); + sc_monitor_destroy(&buffer->buffer_monitor); + sc_mem_free(buffer); +} + + +// Добавление данных в буфер +sc_bool _sc_dictionary_fs_memory_buffer_append( + sc_write_buffer * buffer, + sc_char const * data, + sc_uint64 size) +{ + sc_monitor_acquire_write(&buffer->buffer_monitor); + if (buffer->size + size > buffer->capacity) + { + sc_monitor_release_write(&buffer->buffer_monitor); + return SC_FALSE; // Буфер переполнен + } + sc_mem_cpy(buffer->data + buffer->size, data, size); + buffer->size += size; + sc_monitor_release_write(&buffer->buffer_monitor); + return SC_TRUE; +} + + + +// Сброс буфера на диск +sc_dictionary_fs_memory_status _sc_dictionary_fs_memory_buffer_flush( + sc_dictionary_fs_memory * memory, + sc_write_buffer * buffer, + sc_io_channel * channel, + sc_monitor * channel_monitor) +{ + sc_monitor_acquire_write(&buffer->buffer_monitor); + if (buffer->size == 0) + { + sc_monitor_release_write(&buffer->buffer_monitor); + return SC_FS_MEMORY_OK; + } + + sc_monitor_acquire_write(channel_monitor); + sc_uint64 written_bytes = 0; + if (sc_io_channel_write_chars(channel, buffer->data, buffer->size, &written_bytes, null_ptr) + != SC_FS_IO_STATUS_NORMAL + || written_bytes != buffer->size) + { + sc_fs_memory_error("Error while flushing buffer to disk"); + sc_monitor_release_write(channel_monitor); + sc_monitor_release_write(&buffer->buffer_monitor); + return SC_FS_MEMORY_WRITE_ERROR; + } + memory->last_string_offset += written_bytes; + buffer->size = 0; // Очищаем буфер + sc_monitor_release_write(channel_monitor); + sc_monitor_release_write(&buffer->buffer_monitor); + return SC_FS_MEMORY_OK; +} + + + + + sc_io_channel * _sc_dictionary_fs_memory_get_strings_channel_by_offset( sc_dictionary_fs_memory * memory, sc_uint64 strings_offset, @@ -156,6 +237,9 @@ sc_dictionary_fs_memory_status sc_dictionary_fs_memory_initialize_ext( _sc_number_dictionary_initialize(&(*memory)->string_offsets_link_hashes_dictionary); static sc_char const * string_offsets_link_hashes = "string_offsets_link_hashes" SC_FS_EXT; sc_fs_concat_path((*memory)->path, string_offsets_link_hashes, &(*memory)->string_offsets_link_hashes_path); + + // Инициализация буфера + _sc_dictionary_fs_memory_buffer_init(&(*memory)->write_buffer, BUFFER_SIZE_LIMIT); } sc_fs_memory_info("Configuration:"); sc_message("\tSc-dictionary node size: %zd", sizeof(sc_dictionary_node)); @@ -171,7 +255,6 @@ sc_dictionary_fs_memory_status sc_dictionary_fs_memory_initialize_ext( sc_message("\tTerm separators: \"%s\"", (*memory)->term_separators); sc_fs_memory_info("Successfully initialized"); - return SC_FS_MEMORY_OK; error: @@ -203,25 +286,29 @@ sc_dictionary_fs_memory_status sc_dictionary_fs_memory_shutdown(sc_dictionary_fs sc_fs_memory_info("Shutdown"); { - sc_mem_free(memory->path); - + // Сброс буфера перед завершением + for (sc_uint64 i = 0; i < memory->max_strings_channels && memory->strings_channels[i] != null_ptr; ++i) { - sc_dictionary_destroy(memory->terms_string_offsets_dictionary, _sc_dictionary_fs_memory_node_clear); - sc_mem_free(memory->terms_string_offsets_path); - - for (sc_uint64 i = 0; i < memory->max_strings_channels && memory->strings_channels[i] != null_ptr; ++i) - { - sc_io_channel_shutdown(memory->strings_channels[i], SC_TRUE, null_ptr); - } - sc_mem_free(memory->strings_channels); - _sc_monitor_table_destroy(&memory->strings_channels_monitors_table); - sc_monitor_destroy(&memory->monitor); - sc_monitor_destroy(&memory->resolve_string_offset_monitor); + sc_monitor * channel_monitor = + sc_monitor_table_get_monitor_from_table(&memory->strings_channels_monitors_table, (sc_pointer)i); + _sc_dictionary_fs_memory_buffer_flush( + memory, memory->write_buffer, memory->strings_channels[i], channel_monitor); + sc_io_channel_shutdown(memory->strings_channels[i], SC_TRUE, null_ptr); } + sc_mem_free(memory->path); + sc_dictionary_destroy(memory->terms_string_offsets_dictionary, _sc_dictionary_fs_memory_node_clear); + sc_mem_free(memory->terms_string_offsets_path); + sc_mem_free(memory->strings_channels); + _sc_monitor_table_destroy(&memory->strings_channels_monitors_table); + sc_monitor_destroy(&memory->monitor); + sc_monitor_destroy(&memory->resolve_string_offset_monitor); sc_dictionary_destroy(memory->link_hashes_string_offsets_dictionary, _sc_dictionary_fs_memory_string_node_clear); sc_dictionary_destroy(memory->string_offsets_link_hashes_dictionary, _sc_dictionary_fs_memory_link_node_clear); sc_mem_free(memory->string_offsets_link_hashes_path); + + // Уничтожение буфера + _sc_dictionary_fs_memory_buffer_destroy(memory->write_buffer); } sc_mem_free(memory); @@ -416,60 +503,73 @@ sc_dictionary_fs_memory_status _sc_dictionary_fs_memory_write_string( if (strings_channel == null_ptr) goto no_last_channel_error; - // find string if it exists in fs-memory + // Проверяем, существует ли строка if (is_searchable_string) { *string_offset = _sc_dictionary_fs_memory_get_string_offset_by_string(memory, string, string_size, string_terms->begin->data); } - sc_monitor_acquire_write(&memory->monitor); - sc_monitor_acquire_write(channel_monitor); *is_not_exist = (*string_offset == INVALID_STRING_OFFSET); - // save string in fs-memory if (*is_not_exist) { *string_offset = memory->last_string_offset; - sc_uint64 const normalized_string_offset = _sc_dictionary_fs_memory_normalize_offset(memory, *string_offset); - sc_io_channel_seek(strings_channel, normalized_string_offset, SC_FS_IO_SEEK_SET, null_ptr); - - sc_uint64 written_bytes = 0; - if (sc_io_channel_write_chars(strings_channel, &string_size, sizeof(string_size), &written_bytes, null_ptr) - != SC_FS_IO_STATUS_NORMAL - || sizeof(string_size) != written_bytes) + // Добавляем размер строки и строку в буфер + sc_monitor_acquire_write(&memory->monitor); + if (!_sc_dictionary_fs_memory_buffer_append(memory->write_buffer, (sc_char *)&string_size, sizeof(string_size))) { - sc_fs_memory_error("Error while attribute `size` writing"); - goto write_error; + // Буфер заполнен, сбрасываем его + sc_dictionary_fs_memory_status status = + _sc_dictionary_fs_memory_buffer_flush(memory, memory->write_buffer, strings_channel, channel_monitor); + if (status != SC_FS_MEMORY_OK) + { + sc_monitor_release_write(&memory->monitor); + goto write_error; + } + // Повторяем попытку добавления + if (!_sc_dictionary_fs_memory_buffer_append(memory->write_buffer, (sc_char *)&string_size, sizeof(string_size))) + { + sc_fs_memory_error("Failed to append string size to buffer after flush"); + sc_monitor_release_write(&memory->monitor); + goto write_error; + } } - - memory->last_string_offset += written_bytes; - - if (sc_io_channel_write_chars(strings_channel, string, string_size, &written_bytes, null_ptr) - != SC_FS_IO_STATUS_NORMAL - || string_size != written_bytes) + if (!_sc_dictionary_fs_memory_buffer_append(memory->write_buffer, string, string_size)) { - sc_fs_memory_error("Error while attribute `string` writing"); - goto write_error; + // Буфер заполнен, сбрасываем его + sc_dictionary_fs_memory_status status = + _sc_dictionary_fs_memory_buffer_flush(memory, memory->write_buffer, strings_channel, channel_monitor); + if (status != SC_FS_MEMORY_OK) + { + sc_monitor_release_write(&memory->monitor); + goto write_error; + } + // Повторяем попытку добавления + if (!_sc_dictionary_fs_memory_buffer_append(memory->write_buffer, string, string_size)) + { + sc_fs_memory_error("Failed to append string to buffer after flush"); + sc_monitor_release_write(&memory->monitor); + goto write_error; + } } - - memory->last_string_offset += written_bytes; + sc_monitor_release_write(&memory->monitor); } - sc_monitor_release_write(channel_monitor); - sc_monitor_release_write(&memory->monitor); sc_monitor_release_write(&memory->resolve_string_offset_monitor); return SC_FS_MEMORY_OK; write_error: - sc_monitor_release_write(channel_monitor); - sc_monitor_release_write(&memory->monitor); + sc_monitor_release_write(&memory->resolve_string_offset_monitor); + return SC_FS_MEMORY_WRITE_ERROR; no_last_channel_error: sc_monitor_release_write(&memory->resolve_string_offset_monitor); return SC_FS_MEMORY_WRITE_ERROR; } + + sc_dictionary_fs_memory_status _sc_dictionary_fs_memory_write_string_terms_string_offset( sc_dictionary_fs_memory * memory, sc_uint64 const string_offset, @@ -1698,6 +1798,17 @@ sc_dictionary_fs_memory_status sc_dictionary_fs_memory_save(sc_dictionary_fs_mem } sc_fs_memory_info("Save sc-fs-memory dictionaries"); + // Сбрасываем буфер перед сохранением + for (sc_uint64 i = 0; i < memory->max_strings_channels && memory->strings_channels[i] != null_ptr; ++i) + { + sc_monitor * channel_monitor = + sc_monitor_table_get_monitor_from_table(&memory->strings_channels_monitors_table, (sc_pointer)i); + sc_dictionary_fs_memory_status status = + _sc_dictionary_fs_memory_buffer_flush(memory, memory->write_buffer, memory->strings_channels[i], channel_monitor); + if (status != SC_FS_MEMORY_OK) + return status; + } + sc_dictionary_fs_memory_status status = _sc_dictionary_fs_memory_save_term_string_offsets(memory); if (status != SC_FS_MEMORY_OK) return status; @@ -1707,7 +1818,6 @@ sc_dictionary_fs_memory_status sc_dictionary_fs_memory_save(sc_dictionary_fs_mem return status; sc_message("\tLast string offset: %" PRIu64, memory->last_string_offset); - sc_fs_memory_info("All sc-fs-memory dictionaries saved"); return status; } diff --git a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_dictionary_fs_memory_private.h b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_dictionary_fs_memory_private.h index 6747292d6..c9bfcb5fe 100644 --- a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_dictionary_fs_memory_private.h +++ b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_dictionary_fs_memory_private.h @@ -1,8 +1,8 @@ /* - * This source file is part of an OSTIS project. For the latest info, see http://ostis.net - * Distributed under the MIT License - * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) - */ +* This source file is part of an OSTIS project. For the latest info, see http://ostis.net +* Distributed under the MIT License +* (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) +*/ #ifndef _sc_dictionary_fs_memory_private_h_ #define _sc_dictionary_fs_memory_private_h_ @@ -17,6 +17,8 @@ #include "sc-store/sc-base/sc_monitor_private.h" #include "sc-store/sc-base/sc_monitor_table_private.h" #include "sc-store/sc-base/sc_message.h" +#include "sc_dictionary_fs_memory.h" +#include "sc_io.h" #define SC_FS_EXT ".scdb" #define INVALID_STRING_OFFSET LONG_MAX @@ -26,31 +28,49 @@ #define sc_fs_memory_warning(...) sc_warning(SC_FS_MEMORY_PREFIX __VA_ARGS__) #define sc_fs_memory_error(...) sc_critical(SC_FS_MEMORY_PREFIX __VA_ARGS__) + + + + + +// Структура для буфера записи +typedef struct _sc_write_buffer +{ + sc_char * data; // Массив байтов для хранения данных + sc_uint64 size; // Текущий размер данных в буфере + sc_uint64 capacity; // Максимальный размер буфера + sc_monitor buffer_monitor; // Монитор для синхронизации доступа +} sc_write_buffer; + + struct _sc_dictionary_fs_memory { - sc_char * path; // path to all dictionary files - sc_bool clear; - - sc_uint16 max_strings_channels; - sc_uint32 max_strings_channel_size; - sc_uint32 max_searchable_string_size; // maximal size of strings that can be found by string/substring - sc_char const * term_separators; - sc_bool search_by_substring; - - void ** strings_channels; - sc_monitor_table strings_channels_monitors_table; - sc_uint64 last_string_offset; // last offset of string in 'string_path` - sc_monitor monitor; - sc_monitor resolve_string_offset_monitor; - - sc_char * terms_string_offsets_path; // path to dictionary file with terms and its strings offsets - sc_dictionary * terms_string_offsets_dictionary; // dictionary instance with terms and its strings offsets - - sc_char * string_offsets_link_hashes_path; // path to dictionary file with strings offsets and its link hashes - sc_dictionary * - string_offsets_link_hashes_dictionary; // dictionary instance with strings offsets and its link hashes - sc_dictionary * - link_hashes_string_offsets_dictionary; // dictionary instance with link hashes and its strings offsets + sc_char * path; // path to all dictionary files + sc_bool clear; + + sc_uint16 max_strings_channels; + sc_uint32 max_strings_channel_size; + sc_uint32 max_searchable_string_size; // maximal size of strings that can be found by string/substring + sc_char const * term_separators; + sc_bool search_by_substring; + + void ** strings_channels; + sc_monitor_table strings_channels_monitors_table; + sc_uint64 last_string_offset; // last offset of string in 'string_path` + sc_monitor monitor; + sc_monitor resolve_string_offset_monitor; + + sc_char * terms_string_offsets_path; // path to dictionary file with terms and its strings offsets + sc_dictionary * terms_string_offsets_dictionary; // dictionary instance with terms and its strings offsets + + sc_char * string_offsets_link_hashes_path; // path to dictionary file with strings offsets and its link hashes + sc_dictionary * + string_offsets_link_hashes_dictionary; // dictionary instance with strings offsets and its link hashes + sc_dictionary * + link_hashes_string_offsets_dictionary; // dictionary instance with link hashes and its strings offsets + + sc_write_buffer * write_buffer; // Буфер для группировки операций записи + }; sc_bool _sc_uchar_dictionary_initialize(sc_dictionary ** dictionary); @@ -69,4 +89,19 @@ sc_char * _sc_dictionary_fs_memory_get_first_term(sc_char const * string, sc_cha sc_list * _sc_dictionary_fs_memory_get_string_terms(sc_char const * string, sc_char const * term_separators); -#endif + + + +void _sc_dictionary_fs_memory_buffer_init(sc_write_buffer ** buffer, sc_uint64 capacity); +void _sc_dictionary_fs_memory_buffer_destroy(sc_write_buffer * buffer); +sc_bool _sc_dictionary_fs_memory_buffer_append( + sc_write_buffer * buffer, + sc_char const * data, + sc_uint64 size); +sc_dictionary_fs_memory_status _sc_dictionary_fs_memory_buffer_flush( + sc_dictionary_fs_memory * memory, + sc_write_buffer * buffer, + sc_io_channel * channel, + sc_monitor * channel_monitor); + +#endif \ No newline at end of file diff --git a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.c b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.c index cbb596cb1..93435862c 100644 --- a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.c +++ b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.c @@ -1,8 +1,8 @@ /* - * This source file is part of an OSTIS project. For the latest info, see http://ostis.net - * Distributed under the MIT License - * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) - */ +* This source file is part of an OSTIS project. For the latest info, see http://ostis.net +* Distributed under the MIT License +* (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) +*/ #include "sc_file_system.h" @@ -20,189 +20,189 @@ sc_bool sc_fs_create_file(sc_char const * path) { - if (path == null_ptr) - return SC_FALSE; + if (path == null_ptr) + return SC_FALSE; - sc_io_channel * channel = sc_io_new_write_channel(path, null_ptr); - if (channel == null_ptr) - return SC_FALSE; + sc_io_channel * channel = sc_io_new_write_channel(path, null_ptr); + if (channel == null_ptr) + return SC_FALSE; - sc_io_channel_shutdown(channel, SC_TRUE, null_ptr); - return SC_TRUE; + sc_io_channel_shutdown(channel, SC_TRUE, null_ptr); + return SC_TRUE; } sc_bool sc_fs_copy_file(sc_char const * path, sc_char const * target_path) { - sc_io_channel * read_channel = sc_io_new_read_channel(path, null_ptr); - if (read_channel == null_ptr) - return SC_FALSE; - sc_io_channel_set_encoding(read_channel, null_ptr, null_ptr); - - sc_io_channel * write_channel = sc_io_new_write_channel(target_path, null_ptr); - if (write_channel == null_ptr) - return SC_FALSE; - sc_io_channel_set_encoding(write_channel, null_ptr, null_ptr); - - sc_uint64 read_bytes, written_bytes; - while (SC_TRUE) - { - sc_uint64 string_size = 128; - sc_char string[string_size + 1]; - GIOStatus const status = sc_io_channel_read_chars(read_channel, string, string_size, &read_bytes, null_ptr); - string[string_size] = '\0'; - - if (sc_io_channel_write_chars(write_channel, string, read_bytes, &written_bytes, null_ptr) != SC_FS_IO_STATUS_NORMAL - || written_bytes != read_bytes) - goto error; - - if (status != SC_FS_IO_STATUS_NORMAL) - break; - } - - sc_io_channel_shutdown(read_channel, SC_TRUE, null_ptr); - sc_io_channel_shutdown(write_channel, SC_TRUE, null_ptr); - return SC_TRUE; + sc_io_channel * read_channel = sc_io_new_read_channel(path, null_ptr); + if (read_channel == null_ptr) + return SC_FALSE; + sc_io_channel_set_encoding(read_channel, null_ptr, null_ptr); + + sc_io_channel * write_channel = sc_io_new_write_channel(target_path, null_ptr); + if (write_channel == null_ptr) + return SC_FALSE; + sc_io_channel_set_encoding(write_channel, null_ptr, null_ptr); + + sc_uint64 read_bytes, written_bytes; + while (SC_TRUE) + { + sc_uint64 string_size = 128; + sc_char string[string_size + 1]; + GIOStatus const status = sc_io_channel_read_chars(read_channel, string, string_size, &read_bytes, null_ptr); + string[string_size] = '\0'; + + if (sc_io_channel_write_chars(write_channel, string, read_bytes, &written_bytes, null_ptr) != SC_FS_IO_STATUS_NORMAL + || written_bytes != read_bytes) + goto error; + + if (status != SC_FS_IO_STATUS_NORMAL) + break; + } + + sc_io_channel_shutdown(read_channel, SC_TRUE, null_ptr); + sc_io_channel_shutdown(write_channel, SC_TRUE, null_ptr); + return SC_TRUE; error: - sc_io_channel_shutdown(read_channel, SC_TRUE, null_ptr); - sc_io_channel_shutdown(write_channel, SC_TRUE, null_ptr); - return SC_FALSE; + sc_io_channel_shutdown(read_channel, SC_TRUE, null_ptr); + sc_io_channel_shutdown(write_channel, SC_TRUE, null_ptr); + return SC_FALSE; } sc_bool sc_fs_remove_file(sc_char const * path) { - if (!sc_fs_is_file(path) || g_remove(path) == -1) - return SC_FALSE; + if (!sc_fs_is_file(path) || g_remove(path) == -1) + return SC_FALSE; - return SC_TRUE; + return SC_TRUE; } sc_bool sc_fs_rename_file(sc_char const * old_path, sc_char const * new_path) { - return g_rename(old_path, new_path) == 0; + return g_rename(old_path, new_path) == 0; } sc_bool sc_fs_is_file(sc_char const * path) { - return g_file_test(path, G_FILE_TEST_IS_REGULAR); + return g_file_test(path, G_FILE_TEST_IS_REGULAR); } sc_bool sc_fs_is_binary_file(sc_char const * file_path) { - sc_char command_prefix[] = SC_FS_FILE_COMMAND; - sc_char * command = sc_mem_new(sc_char, sc_str_len(command_prefix) + sc_str_len(file_path) + 1); - strcat(command, command_prefix); - strcat(command, file_path); - - sc_char * result = sc_fs_execute(command); - sc_mem_free(command); - sc_bool const is_binary_file = sc_str_has_prefix(result, "binary"); - sc_mem_free(result); - return is_binary_file; + sc_char command_prefix[] = SC_FS_FILE_COMMAND; + sc_char * command = sc_mem_new(sc_char, sc_str_len(command_prefix) + sc_str_len(file_path) + 1); + strcat(command, command_prefix); + strcat(command, file_path); + + sc_char * result = sc_fs_execute(command); + sc_mem_free(command); + sc_bool const is_binary_file = sc_str_has_prefix(result, "binary"); + sc_mem_free(result); + return is_binary_file; } void sc_fs_get_file_content(sc_char const * file_path, sc_char ** content, sc_uint32 * content_size) { - sc_stream * stream = sc_stream_file_new(file_path, SC_STREAM_FLAG_READ); - if (stream == null_ptr) - { - *content = null_ptr; - *content_size = 0; - return; - } - - sc_stream_get_data(stream, content, content_size); - sc_stream_free(stream); + sc_stream * stream = sc_stream_file_new(file_path, SC_STREAM_FLAG_READ); + if (stream == null_ptr) + { + *content = null_ptr; + *content_size = 0; + return; + } + + sc_stream_get_data(stream, content, content_size); + sc_stream_free(stream); } void sc_fs_concat_path(sc_char const * path, sc_char const * postfix, sc_char ** out_path) { - sc_uint32 size = sc_str_len(path) + sc_str_len(postfix) + 2; - *out_path = sc_mem_new(sc_char, size + 1); - sc_str_printf(*out_path, size, "%s/%s", path, postfix); + sc_uint32 size = sc_str_len(path) + sc_str_len(postfix) + 2; + *out_path = sc_mem_new(sc_char, size + 1); + sc_str_printf(*out_path, size, "%s/%s", path, postfix); } void sc_fs_concat_path_ext(sc_char const * path, sc_char const * postfix, sc_char const * ext, sc_char ** out_path) { - sc_uint32 size = sc_str_len(path) + sc_str_len(postfix) + sc_str_len(ext) + 2; - *out_path = sc_mem_new(sc_char, size + 1); - sc_str_printf(*out_path, size, "%s/%s%s", path, postfix, ext); + sc_uint32 size = sc_str_len(path) + sc_str_len(postfix) + sc_str_len(ext) + 2; + *out_path = sc_mem_new(sc_char, size + 1); + sc_str_printf(*out_path, size, "%s/%s%s", path, postfix, ext); } sc_bool sc_fs_create_directory(sc_char const * path) { #if SC_PLATFORM_LINUX || SC_PLATFORM_MAC - int const mode = 0777; + int const mode = 0777; #else - int const mode = 0; + int const mode = 0; #endif - if (g_mkdir_with_parents(path, mode) == -1) - return SC_FALSE; + if (g_mkdir_with_parents(path, mode) == -1) + return SC_FALSE; - return SC_TRUE; + return SC_TRUE; } sc_bool sc_fs_remove_directory_ext(sc_char const * path, sc_bool remove_root) { - if (sc_fs_is_directory(path) == SC_FALSE) - return SC_FALSE; - - GDir * directory = g_dir_open(path, 0, null_ptr); - - // calculate files - sc_char tmp_path[MAX_PATH_LENGTH]; - sc_char const * file = g_dir_read_name(directory); - while (file != null_ptr) - { - sc_str_printf(tmp_path, MAX_PATH_LENGTH, "%s/%s", path, file); - - if (sc_fs_is_file(tmp_path) == SC_TRUE) - { - if (g_remove(tmp_path) == -1) - return SC_FALSE; - } - else if (sc_fs_is_directory(tmp_path) == SC_TRUE) - sc_fs_remove_directory(tmp_path); - - file = g_dir_read_name(directory); - } - - g_dir_close(directory); - if (remove_root) - g_rmdir(path); - - return SC_TRUE; + if (sc_fs_is_directory(path) == SC_FALSE) + return SC_FALSE; + + GDir * directory = g_dir_open(path, 0, null_ptr); + + // calculate files + sc_char tmp_path[MAX_PATH_LENGTH]; + sc_char const * file = g_dir_read_name(directory); + while (file != null_ptr) + { + sc_str_printf(tmp_path, MAX_PATH_LENGTH, "%s/%s", path, file); + + if (sc_fs_is_file(tmp_path) == SC_TRUE) + { + if (g_remove(tmp_path) == -1) + return SC_FALSE; + } + else if (sc_fs_is_directory(tmp_path) == SC_TRUE) + sc_fs_remove_directory(tmp_path); + + file = g_dir_read_name(directory); + } + + g_dir_close(directory); + if (remove_root) + g_rmdir(path); + + return SC_TRUE; } sc_bool sc_fs_remove_directory(sc_char const * path) { - return sc_fs_remove_directory_ext(path, SC_TRUE); + return sc_fs_remove_directory_ext(path, SC_TRUE); } sc_bool sc_fs_is_directory(sc_char const * path) { - return g_file_test(path, G_FILE_TEST_IS_DIR); + return g_file_test(path, G_FILE_TEST_IS_DIR); } void * sc_fs_new_tmp_write_channel(sc_char const * path, sc_char ** tmp_file_name, sc_char * prefix) { - *tmp_file_name = g_strdup_printf("%s/%s_%lu", path, prefix, (sc_ulong)g_get_real_time()); + *tmp_file_name = g_strdup_printf("%s/%s_%lu", path, prefix, (sc_ulong)g_get_real_time()); - sc_io_channel * result = sc_io_new_write_channel(*tmp_file_name, null_ptr); - return result; + sc_io_channel * result = sc_io_new_write_channel(*tmp_file_name, null_ptr); + return result; } sc_char * sc_fs_execute(sc_char const * command) { - FILE * pipe = popen(command, "r"); - sc_int32 buffer_size = 50; - sc_char buffer[buffer_size]; - while (fgets(buffer, buffer_size, pipe) != null_ptr) - ; - - pclose(pipe); - - sc_char * char_result; - sc_str_cpy(char_result, buffer, sc_str_len(buffer)); - return char_result; -} + FILE * pipe = popen(command, "r"); + sc_int32 buffer_size = 50; + sc_char buffer[buffer_size]; + while (fgets(buffer, buffer_size, pipe) != null_ptr) + ; + + pclose(pipe); + + sc_char * char_result; + sc_str_cpy(char_result, buffer, sc_str_len(buffer)); + return char_result; +} \ No newline at end of file diff --git a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.h b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.h index ed4626b1a..9ef74aa3e 100644 --- a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.h +++ b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_file_system.h @@ -1,8 +1,8 @@ /* - * This source file is part of an OSTIS project. For the latest info, see http://ostis.net - * Distributed under the MIT License - * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) - */ +* This source file is part of an OSTIS project. For the latest info, see http://ostis.net +* Distributed under the MIT License +* (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) +*/ #ifndef _sc_file_system_h_ #define _sc_file_system_h_ @@ -39,4 +39,4 @@ void * sc_fs_new_tmp_write_channel(sc_char const * path, sc_char ** tmp_file_nam sc_char * sc_fs_execute(sc_char const * command); -#endif +#endif \ No newline at end of file diff --git a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c index 43afc79f4..5ace2a01e 100644 --- a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c +++ b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.c @@ -1,8 +1,11 @@ /* - * This source file is part of an OSTIS project. For the latest info, see http://ostis.net - * Distributed under the MIT License - * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) - */ +* This source file is part of an OSTIS project. For the latest info, see http://ostis.net +* Distributed under the MIT License +* (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) +*/ + + +#define _GNU_SOURCE // Необходимо для O_DIRECT #include "sc_fs_memory.h" #include "sc_fs_memory_builder.h" @@ -19,410 +22,488 @@ sc_fs_memory_manager * manager; sc_fs_memory_status sc_fs_memory_initialize_ext(sc_memory_params const * params) { - manager = sc_fs_memory_build(); - manager->version = params->version; - manager->path = params->storage; - - if (manager->path == null_ptr) - { - sc_fs_memory_error("Empty repo path to initialize memory"); - return SC_FS_MEMORY_NO; - } - - static sc_char const * segments_postfix = "segments" SC_FS_EXT; - sc_fs_concat_path(manager->path, segments_postfix, &manager->segments_path); - - if (manager->initialize(&manager->fs_memory, params) != SC_FS_MEMORY_OK) - return SC_FS_MEMORY_NO; - - // clear repository if it needs - if (params->clear == SC_TRUE) - { - sc_fs_memory_info("Clear sc-memory segments"); - if (sc_fs_remove_file(manager->segments_path) == SC_FALSE) - sc_fs_memory_info("Can't remove segments file: %s", manager->segments_path); - } - - return SC_FS_MEMORY_OK; + manager = sc_fs_memory_build(); + manager->version = params->version; + manager->path = params->storage; + + if (manager->path == null_ptr) + { + sc_fs_memory_error("Empty repo path to initialize memory"); + return SC_FS_MEMORY_NO; + } + + static sc_char const * segments_postfix = "segments" SC_FS_EXT; + sc_fs_concat_path(manager->path, segments_postfix, &manager->segments_path); + + if (manager->initialize(&manager->fs_memory, params) != SC_FS_MEMORY_OK) + return SC_FS_MEMORY_NO; + + // clear repository if it needs + if (params->clear == SC_TRUE) + { + sc_fs_memory_info("Clear sc-memory segments"); + if (sc_fs_remove_file(manager->segments_path) == SC_FALSE) + sc_fs_memory_info("Can't remove segments file: %s", manager->segments_path); + } + + return SC_FS_MEMORY_OK; } sc_fs_memory_status sc_fs_memory_initialize(sc_char const * path, sc_bool clear) { - sc_memory_params * params = _sc_dictionary_fs_memory_get_default_params(path, clear); - sc_fs_memory_status const status = sc_fs_memory_initialize_ext(params); - sc_mem_free(params); - return status; + sc_memory_params * params = _sc_dictionary_fs_memory_get_default_params(path, clear); + sc_fs_memory_status const status = sc_fs_memory_initialize_ext(params); + sc_mem_free(params); + return status; } sc_fs_memory_status sc_fs_memory_shutdown() { - sc_fs_memory_status const result = manager->shutdown(manager->fs_memory); - sc_mem_free(manager->segments_path); - sc_mem_free(manager); - return result; + sc_fs_memory_status const result = manager->shutdown(manager->fs_memory); + sc_mem_free(manager->segments_path); + sc_mem_free(manager); + return result; } sc_fs_memory_status sc_fs_memory_link_string( - sc_addr_hash const link_hash, - sc_char const * string, - sc_uint32 const string_size) + sc_addr_hash const link_hash, + sc_char const * string, + sc_uint32 const string_size) { - return manager->link_string(manager->fs_memory, link_hash, string, string_size, SC_TRUE); + return manager->link_string(manager->fs_memory, link_hash, string, string_size, SC_TRUE); } sc_fs_memory_status sc_fs_memory_link_string_ext( - sc_addr_hash const link_hash, - sc_char const * string, - sc_uint32 const string_size, - sc_bool is_searchable_string) + sc_addr_hash const link_hash, + sc_char const * string, + sc_uint32 const string_size, + sc_bool is_searchable_string) { - return manager->link_string(manager->fs_memory, link_hash, string, string_size, is_searchable_string); + return manager->link_string(manager->fs_memory, link_hash, string, string_size, is_searchable_string); } sc_fs_memory_status sc_fs_memory_get_string_by_link_hash( - sc_addr_hash const link_hash, - sc_char ** string, - sc_uint32 * string_size) + sc_addr_hash const link_hash, + sc_char ** string, + sc_uint32 * string_size) { - sc_uint64 size; - sc_fs_memory_status result = manager->get_string_by_link_hash(manager->fs_memory, link_hash, string, &size); - *string_size = size; - return result; + sc_uint64 size; + sc_fs_memory_status result = manager->get_string_by_link_hash(manager->fs_memory, link_hash, string, &size); + *string_size = size; + return result; } sc_fs_memory_status sc_fs_memory_get_link_hashes_by_string( - sc_char const * string, - sc_uint32 const string_size, - sc_link_handler * link_handler) + sc_char const * string, + sc_uint32 const string_size, + sc_link_handler * link_handler) { - return manager->get_link_hashes_by_string(manager->fs_memory, string, string_size, link_handler); + return manager->get_link_hashes_by_string(manager->fs_memory, string, string_size, link_handler); } sc_fs_memory_status sc_fs_memory_get_link_hashes_by_substring( - sc_char const * substring, - sc_uint32 const substring_size, - sc_uint32 const max_length_to_search_as_prefix, - sc_link_handler * link_handler) + sc_char const * substring, + sc_uint32 const substring_size, + sc_uint32 const max_length_to_search_as_prefix, + sc_link_handler * link_handler) { - return manager->get_link_hashes_by_substring( - manager->fs_memory, substring, substring_size, max_length_to_search_as_prefix, link_handler); + return manager->get_link_hashes_by_substring( + manager->fs_memory, substring, substring_size, max_length_to_search_as_prefix, link_handler); } sc_fs_memory_status sc_fs_memory_get_strings_by_substring( - sc_char const * substring, - sc_uint32 const substring_size, - sc_uint32 const max_length_to_search_as_prefix, - sc_link_handler * link_handler) + sc_char const * substring, + sc_uint32 const substring_size, + sc_uint32 const max_length_to_search_as_prefix, + sc_link_handler * link_handler) { - return manager->get_strings_by_substring( - manager->fs_memory, substring, substring_size, max_length_to_search_as_prefix, link_handler); + return manager->get_strings_by_substring( + manager->fs_memory, substring, substring_size, max_length_to_search_as_prefix, link_handler); } sc_fs_memory_status sc_fs_memory_unlink_string(sc_addr_hash link_hash) { - return manager->unlink_string(manager->fs_memory, link_hash); + return manager->unlink_string(manager->fs_memory, link_hash); } -// read, write and save methods -sc_fs_memory_status _sc_fs_memory_load_sc_memory_segments(sc_storage * storage) -{ - if (sc_fs_is_file(manager->segments_path) == SC_FALSE) - { +void write_segment_async(int fd, sc_segment* segment, off_t offset) { + io_context_t ctx = 0; + struct iocb cb; + struct iocb* cbs[1]; + struct io_event events[1]; + + if (io_setup(1, &ctx) < 0) { + sc_fs_memory_error("io_setup failed"); + return; + } + + io_prep_pwrite(&cb, fd, segment, sizeof(sc_segment), offset); + cbs[0] = &cb; + + if (io_submit(ctx, 1, cbs) != 1) { + sc_fs_memory_error("io_submit failed"); + io_destroy(ctx); + return; + } + + if (io_getevents(ctx, 1, 1, events, NULL) != 1) { + sc_fs_memory_error("io_getevents failed"); + io_destroy(ctx); + return; + } + + io_destroy(ctx); +} + + +// Функция для выполнения записи в потоке +void* thread_write(void* arg) { + thread_args* args = (thread_args*)arg; + write_segment_async(args->fd, args->segment, args->offset); + free(args); + return NULL; +} + +// Параллельная запись сегментов +void write_segments_parallel(int fd, sc_segment* segments, sc_addr_seg num_segments) { + pthread_t threads[NUM_THREADS]; + sc_addr_seg segments_per_thread = num_segments / NUM_THREADS; + sc_addr_seg remaining_segments = num_segments % NUM_THREADS; + sc_addr_seg segment_idx = 0; + + for (sc_uint32 i = 0; i < NUM_THREADS; i++) { + sc_addr_seg count = segments_per_thread + (i < remaining_segments ? 1 : 0); + if (count == 0) break; + + for (sc_addr_seg j = 0; j < count; j++) { + thread_args* args = malloc(sizeof(thread_args)); + args->fd = fd; + args->segment = &segments[segment_idx]; + args->offset = segment_idx * sizeof(sc_segment); + + if (pthread_create(&threads[i], NULL, thread_write, args) != 0) { + sc_fs_memory_error("pthread_create failed"); + free(args); + continue; + } + segment_idx++; + } + } + + for (sc_uint32 i = 0; i < NUM_THREADS; i++) { + pthread_join(threads[i], NULL); + } +} + + + +sc_fs_memory_status _sc_fs_memory_load_sc_memory_segments(sc_storage * storage) { + if (sc_fs_is_file(manager->segments_path) == SC_FALSE) { storage->segments_count = 0; sc_fs_memory_info("There are no sc-memory segments in %s", manager->segments_path); return SC_FS_MEMORY_OK; } - // open segments - sc_io_channel * segments_channel = sc_io_new_read_channel(manager->segments_path, null_ptr); - sc_io_channel_set_encoding(segments_channel, null_ptr, null_ptr); + int fd = open(manager->segments_path, O_RDONLY); + if (fd < 0) { + sc_fs_memory_error("Failed to open segments file: %s", manager->segments_path); + return SC_FS_MEMORY_READ_ERROR; + } - if (sc_fs_memory_header_read(segments_channel, &manager->header) != SC_FS_MEMORY_OK) - goto error; - storage->segments_count = manager->header.size; + off_t file_size = lseek(fd, 0, SEEK_END); + if (file_size < sizeof(sc_fs_memory_header) + 3 * sizeof(sc_addr_seg)) { + sc_fs_memory_error("Segments file is too small"); + close(fd); + return SC_FS_MEMORY_READ_ERROR; + } + lseek(fd, 0, SEEK_SET); - // backward compatibility with version 0.7.0 - sc_uint64 read_bytes = 0; - sc_bool is_no_deprecated_segments = storage->segments_count == 0; - if (is_no_deprecated_segments) - sc_fs_memory_info("Load sc-memory segments from %s", manager->segments_path); - else - sc_fs_memory_warning("Load deprecated sc-memory segments from %s", manager->segments_path); - - static sc_uint32 const OLD_SC_ELEMENT_SIZE = 36; - sc_uint32 element_size = is_no_deprecated_segments ? sizeof(sc_element) : OLD_SC_ELEMENT_SIZE; - if (is_no_deprecated_segments) - { - if (sc_io_channel_read_chars( - segments_channel, (sc_char *)&storage->segments_count, sizeof(sc_addr_seg), &read_bytes, null_ptr) - != SC_FS_IO_STATUS_NORMAL - || read_bytes != sizeof(sc_addr_seg)) - { - storage->segments_count = 0; - sc_fs_memory_error("Error while attribute `storage->segments_count` reading"); - goto error; - } + sc_fs_memory_header* header = aligned_alloc(PAGE_SIZE, sizeof(sc_fs_memory_header)); + if (!header) { + sc_fs_memory_error("Failed to allocate memory for header"); + close(fd); + return SC_FS_MEMORY_READ_ERROR; + } - if (sc_io_channel_read_chars( - segments_channel, - (sc_char *)&storage->last_not_engaged_segment_num, - sizeof(sc_addr_seg), - &read_bytes, - null_ptr) - != SC_FS_IO_STATUS_NORMAL - || read_bytes != sizeof(sc_addr_seg)) - { - storage->last_not_engaged_segment_num = 0; - sc_fs_memory_error("Error while attribute `storage->last_not_engaged_segment_num` reading"); - goto error; - } + if (read(fd, header, sizeof(sc_fs_memory_header)) != sizeof(sc_fs_memory_header)) { + sc_fs_memory_error("Failed to read header"); + free(header); + close(fd); + return SC_FS_MEMORY_READ_ERROR; + } + manager->header = *header; + storage->segments_count = manager->header.size; + free(header); + + sc_addr_seg* segments_count = aligned_alloc(PAGE_SIZE, sizeof(sc_addr_seg)); + sc_addr_seg* last_not_engaged = aligned_alloc(PAGE_SIZE, sizeof(sc_addr_seg)); + sc_addr_seg* last_released = aligned_alloc(PAGE_SIZE, sizeof(sc_addr_seg)); + if (!segments_count || !last_not_engaged || !last_released) { + sc_fs_memory_error("Failed to allocate memory for metadata"); + free(segments_count); + free(last_not_engaged); + free(last_released); + close(fd); + return SC_FS_MEMORY_READ_ERROR; + } - if (sc_io_channel_read_chars( - segments_channel, - (sc_char *)&storage->last_released_segment_num, - sizeof(sc_addr_seg), - &read_bytes, - null_ptr) - != SC_FS_IO_STATUS_NORMAL - || read_bytes != sizeof(sc_addr_seg)) - { - storage->last_released_segment_num = 0; - sc_fs_memory_error("Error while attribute `storage->last_released_segment_num` reading"); - goto error; - } + if (read(fd, segments_count, sizeof(sc_addr_seg)) != sizeof(sc_addr_seg)) { + sc_fs_memory_error("Failed to read segments_count"); + free(segments_count); + free(last_not_engaged); + free(last_released); + close(fd); + return SC_FS_MEMORY_READ_ERROR; + } + storage->segments_count = *segments_count; + free(segments_count); + + if (read(fd, last_not_engaged, sizeof(sc_addr_seg)) != sizeof(sc_addr_seg)) { + sc_fs_memory_error("Failed to read last_not_engaged_segment_num"); + free(last_not_engaged); + free(last_released); + close(fd); + return SC_FS_MEMORY_READ_ERROR; + } + storage->last_not_engaged_segment_num = *last_not_engaged; + free(last_not_engaged); + + if (read(fd, last_released, sizeof(sc_addr_seg)) != sizeof(sc_addr_seg)) { + sc_fs_memory_error("Failed to read last_released_segment_num"); + free(last_released); + close(fd); + return SC_FS_MEMORY_READ_ERROR; } + storage->last_released_segment_num = *last_released; + free(last_released); sc_version read_version; sc_version_from_int(manager->header.version, &read_version); - if (sc_version_compare(&manager->version, &read_version) == -1) - { - sc_char * version = sc_version_string_new(&read_version); + if (sc_version_compare(&manager->version, &read_version) == -1) { + sc_char* version = sc_version_string_new(&read_version); sc_fs_memory_error("Read sc-memory segments has incompatible version %s", version); sc_version_string_free(version); - goto error; + close(fd); + return SC_FS_MEMORY_READ_ERROR; } + storage->segments = malloc(storage->segments_count * sizeof(sc_segment*)); + if (!storage->segments) { + sc_fs_memory_error("Failed to allocate memory for segments array"); + close(fd); + return SC_FS_MEMORY_READ_ERROR; + } for (sc_addr_seg i = 0; i < storage->segments_count; ++i) - { - sc_addr_seg const num = i; - sc_segment * seg = sc_segment_new(i + 1); - storage->segments[i] = seg; - - for (sc_addr_seg j = 0; j < SC_SEGMENT_ELEMENTS_COUNT; ++j) - { - if (sc_io_channel_read_chars(segments_channel, (sc_char *)&seg->elements[j], element_size, &read_bytes, null_ptr) - != SC_FS_IO_STATUS_NORMAL - || read_bytes != element_size) - { - storage->segments_count = num; - sc_fs_memory_error("Error while sc-element %d in sc-segment %d reading", j, i); - goto error; + storage->segments[i] = null_ptr; + + // Загрузка L2-кэша + sc_char * l2_cache_path = sc_mem_new(sc_char, strlen(manager->path) + 20); + sprintf(l2_cache_path, "%s/cache_segments.dat", manager->path); + int cache_fd = open(l2_cache_path, O_RDONLY); + if (cache_fd >= 0) { + off_t cache_size = lseek(cache_fd, 0, SEEK_END); + lseek(cache_fd, 0, SEEK_SET); + while (lseek(cache_fd, 0, SEEK_CUR) < cache_size) { + struct { sc_addr_seg num; off_t offset; } entry; + if (read(cache_fd, &entry, sizeof(entry)) != sizeof(entry)) { + sc_fs_memory_warning("Failed to read L2 cache entry"); + break; } - - // needed for sc-template search - if (!is_no_deprecated_segments) - { - seg->elements[j].incoming_arcs_count = 1; - seg->elements[j].outgoing_arcs_count = 1; + sc_segment* seg = aligned_alloc(PAGE_SIZE, sizeof(sc_segment)); + if (!seg) { + sc_fs_memory_warning("Failed to allocate segment for L2 cache"); + continue; + } + if (pread(cache_fd, seg, sizeof(sc_segment), entry.offset) != sizeof(sc_segment)) { + sc_fs_memory_warning("Failed to read segment from L2 cache"); + free(seg); + continue; + } + if (entry.num <= storage->segments_count && storage->segments[entry.num - 1] == null_ptr) { + storage->segments[entry.num - 1] = seg; + _sc_l1_cache_add(storage->l1_cache, entry.num, seg); + } else { + free(seg); } } - - if (is_no_deprecated_segments) - { - if (sc_io_channel_read_chars( - segments_channel, (sc_char *)&seg->last_engaged_offset, sizeof(sc_addr_offset), &read_bytes, null_ptr) - != SC_FS_IO_STATUS_NORMAL - || read_bytes != sizeof(sc_addr_offset)) - { - sc_fs_memory_error("Error while sc-segment %d reading", i); - goto error; + close(cache_fd); + } + sc_mem_free(l2_cache_path); + + // Загрузка оставшихся сегментов из основного хранилища + for (sc_addr_seg i = 0; i < storage->segments_count; ++i) { + if (storage->segments[i] != null_ptr) + continue; + + sc_segment* seg = aligned_alloc(PAGE_SIZE, sizeof(sc_segment)); + if (!seg) { + sc_fs_memory_error("Failed to allocate memory for segment %d", i); + for (sc_addr_seg j = 0; j < storage->segments_count; ++j) { + if (storage->segments[j]) + free(storage->segments[j]); } - - if (sc_io_channel_read_chars( - segments_channel, (sc_char *)&seg->last_released_offset, sizeof(sc_addr_offset), &read_bytes, null_ptr) - != SC_FS_IO_STATUS_NORMAL - || read_bytes != sizeof(sc_addr_offset)) - { - sc_fs_memory_error("Error while sc-segment %d reading", i); - goto error; + free(storage->segments); + close(fd); + return SC_FS_MEMORY_READ_ERROR; + } + if (read(fd, seg, sizeof(sc_segment)) != sizeof(sc_segment)) { + sc_fs_memory_error("Failed to read segment %d", i); + free(seg); + for (sc_addr_seg j = 0; j < storage->segments_count; ++j) { + if (storage->segments[j]) + free(storage->segments[j]); } + free(storage->segments); + close(fd); + return SC_FS_MEMORY_READ_ERROR; } - - i = num; + storage->segments[i] = seg; + _sc_l1_cache_add(storage->l1_cache, i + 1, seg); } - sc_io_channel_shutdown(segments_channel, SC_FALSE, null_ptr); - + close(fd); sc_message("\tLoaded segments count: %d", storage->segments_count); sc_message("\tSc-segments size: %ld", storage->segments_count * sizeof(sc_segment)); sc_message("\tLast not engaged segment num: %d", storage->last_not_engaged_segment_num); sc_message("\tLast released segment num: %d", storage->last_released_segment_num); - - if (is_no_deprecated_segments) - sc_fs_memory_info("Sc-memory segments loaded"); - else - sc_fs_memory_warning("Deprecated sc-memory segments loaded"); - + sc_fs_memory_info("Sc-memory segments loaded"); return SC_FS_MEMORY_OK; - -error: -{ - sc_io_channel_shutdown(segments_channel, SC_FALSE, null_ptr); - return SC_FS_MEMORY_READ_ERROR; -} } + + sc_fs_memory_status sc_fs_memory_load(sc_storage * storage) { - if (_sc_fs_memory_load_sc_memory_segments(storage) != SC_FS_MEMORY_OK) - return SC_FS_MEMORY_READ_ERROR; - if (manager->load(manager->fs_memory) != SC_FS_MEMORY_OK) - return SC_FS_MEMORY_READ_ERROR; + if (_sc_fs_memory_load_sc_memory_segments(storage) != SC_FS_MEMORY_OK) + return SC_FS_MEMORY_READ_ERROR; + if (manager->load(manager->fs_memory) != SC_FS_MEMORY_OK) + return SC_FS_MEMORY_READ_ERROR; - return SC_FS_MEMORY_OK; + return SC_FS_MEMORY_OK; } -sc_fs_memory_status _sc_fs_memory_save_sc_memory_segments(sc_storage * storage) -{ + + +sc_fs_memory_status _sc_fs_memory_save_sc_memory_segments(sc_storage * storage) { sc_fs_memory_info("Save sc-memory segments"); - // create temporary file - sc_char * tmp_filename; - sc_io_channel * segments_channel = sc_fs_new_tmp_write_channel(manager->fs_memory->path, &tmp_filename, "segments"); - sc_io_channel_set_encoding(segments_channel, null_ptr, null_ptr); - - manager->header.size = 0; - manager->header.version = sc_version_to_int(&manager->version); - manager->header.timestamp = g_get_real_time(); - if (sc_fs_memory_header_write(segments_channel, manager->header) != SC_FS_MEMORY_OK) - goto error; - - sc_uint64 written_bytes; - if (sc_io_channel_write_chars( - segments_channel, (sc_char *)&storage->segments_count, sizeof(sc_addr_seg), &written_bytes, null_ptr) - != SC_FS_IO_STATUS_NORMAL - || written_bytes != sizeof(sc_addr_seg)) - { - sc_fs_memory_error("Error while attribute `storage->segments_count` writing"); - goto error; + int fd = open(manager->segments_path, O_RDWR | O_CREAT, 0666); + if (fd < 0) { + sc_fs_memory_error("Failed to open segments file: %s", manager->segments_path); + return SC_FS_MEMORY_WRITE_ERROR; } - if (sc_io_channel_write_chars( - segments_channel, - (sc_char *)&storage->last_not_engaged_segment_num, - sizeof(sc_addr_seg), - &written_bytes, - null_ptr) - != SC_FS_IO_STATUS_NORMAL - || written_bytes != sizeof(sc_addr_seg)) - { - sc_fs_memory_error("Error while attribute `storage->last_not_engaged_segment_num` writing"); - goto error; + sc_fs_memory_header* header = aligned_alloc(PAGE_SIZE, sizeof(sc_fs_memory_header)); + if (!header) { + sc_fs_memory_error("Failed to allocate memory for header"); + close(fd); + return SC_FS_MEMORY_WRITE_ERROR; } - - if (sc_io_channel_write_chars( - segments_channel, - (sc_char *)&storage->last_released_segment_num, - sizeof(sc_addr_seg), - &written_bytes, - null_ptr) - != SC_FS_IO_STATUS_NORMAL - || written_bytes != sizeof(sc_addr_seg)) - { - sc_fs_memory_error("Error while attribute `storage->last_released_segment_num` writing"); - goto error; + header->size = storage->segments_count; + header->version = sc_version_to_int(&manager->version); + header->timestamp = g_get_real_time(); + + if (write(fd, header, sizeof(sc_fs_memory_header)) != sizeof(sc_fs_memory_header)) { + sc_fs_memory_error("Failed to write header"); + free(header); + close(fd); + return SC_FS_MEMORY_WRITE_ERROR; + } + free(header); + + sc_addr_seg* segments_count = aligned_alloc(PAGE_SIZE, sizeof(sc_addr_seg)); + sc_addr_seg* last_not_engaged = aligned_alloc(PAGE_SIZE, sizeof(sc_addr_seg)); + sc_addr_seg* last_released = aligned_alloc(PAGE_SIZE, sizeof(sc_addr_seg)); + if (!segments_count || !last_not_engaged || !last_released) { + sc_fs_memory_error("Failed to allocate memory for metadata"); + free(segments_count); + free(last_not_engaged); + free(last_released); + close(fd); + return SC_FS_MEMORY_WRITE_ERROR; } - for (sc_addr_seg idx = 0; idx < storage->segments_count; ++idx) - { - sc_segment * segment = storage->segments[idx]; - if (segment == null_ptr) - { - sc_fs_memory_error("Error while attribute `segment` writing"); - goto error; - } + *segments_count = storage->segments_count; + if (write(fd, segments_count, sizeof(sc_addr_seg)) != sizeof(sc_addr_seg)) { + sc_fs_memory_error("Failed to write segments_count"); + free(segments_count); + free(last_not_engaged); + free(last_released); + close(fd); + return SC_FS_MEMORY_WRITE_ERROR; + } + free(segments_count); + + *last_not_engaged = storage->last_not_engaged_segment_num; + if (write(fd, last_not_engaged, sizeof(sc_addr_seg)) != sizeof(sc_addr_seg)) { + sc_fs_memory_error("Failed to write last_not_engaged_segment_num"); + free(last_not_engaged); + free(last_released); + close(fd); + return SC_FS_MEMORY_WRITE_ERROR; + } + free(last_not_engaged); - sc_monitor_acquire_read(&segment->monitor); + *last_released = storage->last_released_segment_num; + if (write(fd, last_released, sizeof(sc_addr_seg)) != sizeof(sc_addr_seg)) { + sc_fs_memory_error("Failed to write last_released_segment_num"); + free(last_released); + close(fd); + return SC_FS_MEMORY_WRITE_ERROR; + } + free(last_released); - if (sc_io_channel_write_chars( - segments_channel, (sc_char *)segment->elements, SC_SEG_ELEMENTS_SIZE_BYTE, &written_bytes, null_ptr) - != SC_FS_IO_STATUS_NORMAL - || written_bytes != SC_SEG_ELEMENTS_SIZE_BYTE) - { - sc_fs_memory_error("Error while attribute `segment->elements` writing"); - goto segment_save_error; - } + sc_segment* segments = aligned_alloc(PAGE_SIZE, storage->segments_count * sizeof(sc_segment)); + if (!segments) { + sc_fs_memory_error("Failed to allocate memory for segments"); + close(fd); + return SC_FS_MEMORY_WRITE_ERROR; + } - if (sc_io_channel_write_chars( - segments_channel, - (sc_char *)&segment->last_engaged_offset, - sizeof(sc_addr_offset), - &written_bytes, - null_ptr) - != SC_FS_IO_STATUS_NORMAL - || written_bytes != sizeof(sc_addr_offset)) - { - sc_fs_memory_error("Error while attribute `segment->last_engaged_offset` writing"); - goto segment_save_error; + for (sc_addr_seg idx = 0; idx < storage->segments_count; ++idx) { + sc_segment * segment = storage->segments[idx]; + if (segment == null_ptr) { + segment = _sc_l1_cache_get(storage->l1_cache, idx + 1); + if (!segment) + segment = _sc_l2_cache_get(storage->l2_cache, idx + 1); } - - if (sc_io_channel_write_chars( - segments_channel, - (sc_char *)&segment->last_released_offset, - sizeof(sc_addr_offset), - &written_bytes, - null_ptr) - != SC_FS_IO_STATUS_NORMAL - || written_bytes != sizeof(sc_addr_offset)) - { - sc_fs_memory_error("Error while attribute `segment->last_released_offset` writing"); - goto segment_save_error; + if (segment == null_ptr) { + sc_fs_memory_error("Error: segment %d is null", idx); + free(segments); + close(fd); + return SC_FS_MEMORY_WRITE_ERROR; } - - segment_save_error: + sc_monitor_acquire_read(&segment->monitor); + memcpy(&segments[idx], segment, sizeof(sc_segment)); sc_monitor_release_read(&segment->monitor); } - // rename main file - if (sc_fs_is_file(tmp_filename)) - { - if (sc_fs_rename_file(tmp_filename, manager->segments_path) == SC_FALSE) - { - sc_fs_memory_error("Can't rename %s -> %s", tmp_filename, manager->segments_path); - goto error; - } - } + write_segments_parallel(fd, segments, storage->segments_count); - sc_message("\tLoaded segments count: %d", storage->segments_count); + free(segments); + close(fd); + + sc_message("\tSaved segments count: %d", storage->segments_count); sc_message("\tSc-segments size: %ld", storage->segments_count * sizeof(sc_segment)); sc_message("\tLast not engaged segment num: %d", storage->last_not_engaged_segment_num); sc_message("\tLast released segment num: %d", storage->last_released_segment_num); - - sc_mem_free(tmp_filename); - sc_io_channel_shutdown(segments_channel, SC_TRUE, null_ptr); sc_fs_memory_info("Sc-memory segments saved"); return SC_FS_MEMORY_OK; - -error: -{ - sc_mem_free(tmp_filename); - sc_io_channel_shutdown(segments_channel, SC_TRUE, null_ptr); - return SC_FS_MEMORY_WRITE_ERROR; -} } + sc_fs_memory_status sc_fs_memory_save(sc_storage * storage) { - if (manager->path == null_ptr) - { - sc_fs_memory_error("Repo path is empty to save memory"); - return SC_FS_MEMORY_NO; - } - - if (_sc_fs_memory_save_sc_memory_segments(storage) != SC_FS_MEMORY_OK) - return SC_FS_MEMORY_WRITE_ERROR; - if (manager->save(manager->fs_memory) != SC_FS_MEMORY_OK) - return SC_FS_MEMORY_WRITE_ERROR; - - return SC_FS_MEMORY_OK; + if (manager->path == null_ptr) + { + sc_fs_memory_error("Repo path is empty to save memory"); + return SC_FS_MEMORY_NO; + } + + if (_sc_fs_memory_save_sc_memory_segments(storage) != SC_FS_MEMORY_OK) + return SC_FS_MEMORY_WRITE_ERROR; + if (manager->save(manager->fs_memory) != SC_FS_MEMORY_OK) + return SC_FS_MEMORY_WRITE_ERROR; + + return SC_FS_MEMORY_OK; } diff --git a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.h b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.h index 641f621c8..f7c7b6f99 100644 --- a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.h +++ b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_fs_memory.h @@ -4,12 +4,26 @@ * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) */ + + #ifndef _sc_fs_memory_h_ #define _sc_fs_memory_h_ #include "sc_fs_memory_status.h" #include "sc_fs_memory_header.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + + #include "sc-core/sc_types.h" #include "sc-core/sc_defines.h" #include "sc-core/sc_stream.h" @@ -21,6 +35,16 @@ typedef struct _sc_dictionary_fs_memory sc_fs_memory; #endif +#define PAGE_SIZE 4096 +#define NUM_THREADS 4 + +typedef struct { + int fd; + sc_segment* segment; + off_t offset; +} thread_args; + + typedef struct _sc_fs_memory_manager { sc_fs_memory * fs_memory; // file system memory instance diff --git a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_io.h b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_io.h index 5a2aa2bfb..1c7a63da3 100644 --- a/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_io.h +++ b/sc-memory/sc-core/src/sc-store/sc-fs-memory/sc_io.h @@ -1,8 +1,8 @@ /* - * This source file is part of an OSTIS project. For the latest info, see http://ostis.net - * Distributed under the MIT License - * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) - */ +* This source file is part of an OSTIS project. For the latest info, see http://ostis.net +* Distributed under the MIT License +* (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) +*/ #ifndef _sc_io_h_ #define _sc_io_h_ @@ -32,15 +32,15 @@ typedef GIOChannel sc_io_channel; #define sc_io_channel_flush(channel, errors) g_io_channel_flush(channel, errors) #define sc_io_channel_shutdown(channel, flush, errors) \ - g_io_channel_shutdown(channel, flush, errors); \ - g_io_channel_unref(channel) + g_io_channel_shutdown(channel, flush, errors); \ + g_io_channel_unref(channel) #define sc_io_channel_write_chars(channel, chars, count, written_bytes, errors) \ - g_io_channel_write_chars(channel, (sc_char *)chars, count, (gsize *)written_bytes, errors) + g_io_channel_write_chars(channel, (sc_char *)chars, count, (gsize *)written_bytes, errors) #define sc_io_channel_read_chars(channel, chars, count, read_bytes, errors) \ - g_io_channel_read_chars(channel, chars, count, (gsize *)read_bytes, errors) + g_io_channel_read_chars(channel, chars, count, (gsize *)read_bytes, errors) #define sc_io_channel_seek(channel, offset, type, errors) g_io_channel_seek_position(channel, offset, type, errors) -#endif +#endif \ No newline at end of file diff --git a/sc-memory/sc-core/src/sc-store/sc_segment.h b/sc-memory/sc-core/src/sc-store/sc_segment.h index ab97ec068..48eef170e 100644 --- a/sc-memory/sc-core/src/sc-store/sc_segment.h +++ b/sc-memory/sc-core/src/sc-store/sc_segment.h @@ -28,6 +28,7 @@ struct _sc_segment sc_addr_offset last_engaged_offset; // number of sc-element in the segment sc_addr_offset last_released_offset; sc_monitor monitor; + sc_bool is_modified; // Indicates if segment was modified }; /*! Create new segment with specified size. diff --git a/sc-memory/sc-core/src/sc-store/sc_storage.c b/sc-memory/sc-core/src/sc-store/sc_storage.c index 36c7594bd..cb8764fce 100644 --- a/sc-memory/sc-core/src/sc-store/sc_storage.c +++ b/sc-memory/sc-core/src/sc-store/sc_storage.c @@ -1,64 +1,214 @@ /* - * This source file is part of an OSTIS project. For the latest info, see http://ostis.net - * Distributed under the MIT License - * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) - */ +* This source file is part of an OSTIS project. For the latest info, see http://ostis.net +* Distributed under the MIT License +* (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) +*/ -#include "sc_storage.h" -#include "sc-core/sc_event_subscription.h" +#define _GNU_SOURCE // Необходимо для O_DIRECT +#include +#include "sc_storage.h" +#include "sc_storage_private.h" +#include "sc-core/sc_event_subscription.h" #include "sc-core/sc_stream_memory.h" #include "sc-core/sc-base/sc_allocator.h" #include "sc-core/sc-container/sc_string.h" - #include "sc-core/sc_keynodes.h" - #include "sc_segment.h" #include "sc_element.h" - #include "sc-fs-memory/sc_fs_memory.h" - -#include "sc_storage_private.h" #include "sc_memory_private.h" -sc_storage * storage = null_ptr; +struct _sc_storage * storage = null_ptr; + +// Вспомогательная функция для добавления операции в буфер +static sc_bool _sc_storage_add_operation( + sc_operation_type type, + sc_addr addr, + sc_type type_mask, + sc_addr begin, + sc_addr end, + sc_char * content, + sc_uint64 content_size) +{ + sc_monitor_acquire_write(&storage->segment_buffer.monitor); + + if (storage->segment_buffer.size >= storage->segment_buffer.capacity) + { + sc_monitor_release_write(&storage->segment_buffer.monitor); + return SC_FALSE; + } + + sc_operation * op = &storage->segment_buffer.operations[storage->segment_buffer.size++]; + op->type = type; + op->addr = addr; + op->type_mask = type_mask; + op->begin = begin; + op->end = end; + op->content = content ? sc_mem_new(sc_char, content_size + 1) : null_ptr; + if (op->content && content) + sc_mem_cpy(op->content, content, content_size); + op->content_size = content_size; + + sc_monitor_release_write(&storage->segment_buffer.monitor); + return SC_TRUE; +} + +// Вспомогательная функция для обработки буфера операций +static sc_result _sc_storage_flush_buffer() +{ + sc_monitor_acquire_write(&storage->segment_buffer.monitor); + + for (sc_uint32 i = 0; i < storage->segment_buffer.size; ++i) + { + sc_operation * op = &storage->segment_buffer.operations[i]; + sc_segment * segment = null_ptr; + + if (op->addr.seg > 0 && op->addr.seg <= storage->segments_count) + { + // Проверка L1-кэша + segment = _sc_l1_cache_get(storage->l1_cache, op->addr.seg); + if (!segment) + { + // Проверка L2-кэша + segment = _sc_l2_cache_get(storage->l2_cache, op->addr.seg); + if (segment) + { + sc_monitor_acquire_write(&storage->segments_monitor); + storage->segments[op->addr.seg - 1] = segment; + sc_monitor_release_write(&storage->segments_monitor); + _sc_l1_cache_add(storage->l1_cache, op->addr.seg, segment); + } + } + if (!segment) + { + sc_monitor_acquire_read(&storage->segments_monitor); + segment = storage->segments[op->addr.seg - 1]; + sc_monitor_release_read(&storage->segments_monitor); + } + } + + if (!segment && op->type != SC_OP_ERASE_ELEMENT) + { + segment = sc_segment_new(op->addr.seg); + sc_monitor_acquire_write(&storage->segments_monitor); + storage->segments[op->addr.seg - 1] = segment; + if (op->addr.seg > storage->segments_count) + storage->segments_count = op->addr.seg; + sc_monitor_release_write(&storage->segments_monitor); + _sc_l1_cache_add(storage->l1_cache, op->addr.seg, segment); + } + + if (segment) + { + sc_monitor_acquire_write(&segment->monitor); + segment->is_modified = SC_TRUE; + sc_monitor_release_write(&segment->monitor); + } + + switch (op->type) + { + case SC_OP_NODE_NEW: + case SC_OP_LINK_NEW: + if (segment) + { + sc_element * el = &segment->elements[op->addr.offset]; + el->flags.type = op->type_mask; + el->flags.states |= SC_STATE_ELEMENT_EXIST; + } + break; + + case SC_OP_ARC_NEW: + if (segment) + { + sc_element * el = &segment->elements[op->addr.offset]; + el->flags.type = op->type_mask; + el->arc.begin = op->begin; + el->arc.end = op->end; + el->flags.states |= SC_STATE_ELEMENT_EXIST; + } + break; + + case SC_OP_SET_LINK_CONTENT: + // Содержимое уже записано в sc_fs_memory_link_string_ext + break; + + case SC_OP_ERASE_ELEMENT: + if (segment) + { + sc_element * el = &segment->elements[op->addr.offset]; + el->flags.states &= ~SC_STATE_ELEMENT_EXIST; + if (sc_type_has_subtype(el->flags.type, sc_type_node_link)) + sc_fs_memory_unlink_string(SC_ADDR_LOCAL_TO_INT(op->addr)); + } + break; + } + + if (op->content) + sc_mem_free(op->content); + } + + storage->segment_buffer.size = 0; + sc_monitor_release_write(&storage->segment_buffer.monitor); + return SC_RESULT_OK; +} sc_result sc_storage_initialize(sc_memory_params const * params) { if (sc_fs_memory_initialize_ext(params) != SC_FS_MEMORY_OK) return SC_RESULT_ERROR; - storage = sc_mem_new(sc_storage, 1); + storage = sc_mem_new(struct _sc_storage, 1); storage->max_segments_count = params->max_loaded_segments; storage->segments_count = 0; storage->last_not_engaged_segment_num = 0; storage->last_released_segment_num = 0; storage->segments = sc_mem_new(sc_segment *, params->max_loaded_segments); + for (sc_addr_seg i = 0; i < params->max_loaded_segments; ++i) + storage->segments[i] = null_ptr; sc_monitor_init(&storage->segments_monitor); _sc_monitor_table_init(&storage->addr_monitors_table); + // Инициализация буфера операций + storage->segment_buffer.operations = sc_mem_new(sc_operation, SC_BUFFER_MAX_OPERATIONS); + storage->segment_buffer.size = 0; + storage->segment_buffer.capacity = SC_BUFFER_MAX_OPERATIONS; + sc_monitor_init(&storage->segment_buffer.monitor); + + // Инициализация L1-кэша + storage->l1_cache = sc_mem_new(sc_l1_cache, 1); + _sc_l1_cache_init(storage->l1_cache, (sc_uint32)(params->max_loaded_segments * SC_L1_CACHE_CAPACITY_FACTOR)); + + // Инициализация L2-кэша + storage->l2_cache = sc_mem_new(sc_l2_cache, 1); + sc_char * l2_cache_path = sc_mem_new(sc_char, strlen(params->storage) + 20); + sprintf(l2_cache_path, "%s/cache_segments.dat", params->storage); + _sc_l2_cache_init(storage->l2_cache, (sc_uint32)(params->max_loaded_segments * SC_L2_CACHE_CAPACITY_FACTOR), l2_cache_path); + sc_mem_free(l2_cache_path); + sc_memory_info("Sc-memory configuration:"); sc_message("\tClean on initialize: %s", params->clear ? "On" : "Off"); sc_message("\tSc-element size: %zd", sizeof(sc_element)); sc_message("\tSc-segment size: %zd", sizeof(sc_segment)); sc_message("\tSc-segment elements count: %d", SC_SEGMENT_ELEMENTS_COUNT); - sc_message("\tSc-storage size: %zd", sizeof(sc_storage)); + sc_message("\tSc-storage size: %zd", sizeof(struct _sc_storage)); sc_message("\tMax segments count: %d", storage->max_segments_count); + sc_message("\tL1 cache capacity: %d", storage->l1_cache->capacity); + sc_message("\tL2 cache capacity: %d", storage->l2_cache->capacity); storage->processes_segments_table = sc_hash_table_init(g_direct_hash, g_direct_equal, null_ptr, null_ptr); sc_monitor_init(&storage->processes_monitor); - sc_result result = SC_TRUE; + sc_result result = SC_RESULT_OK; if (params->clear == SC_FALSE) { sc_monitor_acquire_write(&storage->segments_monitor); - result = sc_fs_memory_load(storage) == SC_FS_MEMORY_OK; + result = sc_fs_memory_load(storage) == SC_FS_MEMORY_OK ? SC_RESULT_OK : SC_RESULT_ERROR; sc_monitor_release_write(&storage->segments_monitor); } sc_storage_dump_manager_initialize(&storage->dump_manager, params); - sc_event_subscription_manager_initialize(&storage->events_subscription_manager); sc_event_emission_manager_initialize(&storage->events_emission_manager, params); @@ -68,7 +218,7 @@ sc_result sc_storage_initialize(sc_memory_params const * params) sc_result sc_storage_shutdown(sc_bool save_state) { if (storage == null_ptr) - goto error; + return SC_RESULT_NO; sc_event_emission_manager_stop(storage->events_emission_manager); sc_event_emission_manager_shutdown(storage->events_emission_manager); @@ -80,74 +230,90 @@ sc_result sc_storage_shutdown(sc_bool save_state) if (save_state == SC_TRUE) { - if (sc_fs_memory_save(storage) != SC_FS_MEMORY_OK) + if (sc_storage_save(null_ptr) != SC_RESULT_OK) return SC_RESULT_ERROR; } -error: - if (sc_fs_memory_shutdown() != SC_FS_MEMORY_OK) - return SC_RESULT_ERROR; + // Очистка L1-кэша + _sc_l1_cache_destroy(storage->l1_cache); + sc_mem_free(storage->l1_cache); + storage->l1_cache = null_ptr; - if (storage == null_ptr) - return SC_RESULT_NO; + // Очистка L2-кэша + _sc_l2_cache_destroy(storage->l2_cache); + sc_mem_free(storage->l2_cache); + storage->l2_cache = null_ptr; sc_monitor_acquire_write(&storage->processes_monitor); - if (storage->processes_segments_table != null_ptr) { sc_hash_table_destroy(storage->processes_segments_table); storage->processes_segments_table = null_ptr; } - sc_monitor_release_write(&storage->processes_monitor); sc_monitor_destroy(&storage->processes_monitor); sc_monitor_acquire_write(&storage->segments_monitor); - for (sc_addr_seg idx = 0; idx < storage->segments_count; idx++) { sc_segment * segment = storage->segments[idx]; if (segment == null_ptr) continue; sc_segment_free(segment); + storage->segments[idx] = null_ptr; } - sc_monitor_release_write(&storage->segments_monitor); + // Очистка буфера операций + sc_monitor_acquire_write(&storage->segment_buffer.monitor); + for (sc_uint32 i = 0; i < storage->segment_buffer.size; ++i) + { + if (storage->segment_buffer.operations[i].content) + sc_mem_free(storage->segment_buffer.operations[i].content); + } + sc_mem_free(storage->segment_buffer.operations); + storage->segment_buffer.size = 0; + storage->segment_buffer.capacity = 0; + sc_monitor_release_write(&storage->segment_buffer.monitor); + sc_monitor_destroy(&storage->segment_buffer.monitor); + sc_mem_free(storage->segments); + storage->segments = null_ptr; sc_monitor_destroy(&storage->segments_monitor); _sc_monitor_table_destroy(&storage->addr_monitors_table); sc_mem_free(storage); storage = null_ptr; + if (sc_fs_memory_shutdown() != SC_FS_MEMORY_OK) + return SC_RESULT_ERROR; + return SC_RESULT_OK; } sc_bool sc_storage_is_initialized() { - return storage != null_ptr; + return storage != null_ptr; } -sc_storage * sc_storage_get() +struct _sc_storage * sc_storage_get() { - return storage; + return storage; } sc_event_emission_manager * sc_storage_get_event_emission_manager() { - return storage ? storage->events_emission_manager : null_ptr; + return storage ? storage->events_emission_manager : null_ptr; } sc_event_subscription_manager * sc_storage_get_event_subscription_manager() { - return storage ? storage->events_subscription_manager : null_ptr; + return storage ? storage->events_subscription_manager : null_ptr; } sc_bool sc_storage_is_element(sc_memory_context const * ctx, sc_addr addr) { sc_element * el = null_ptr; sc_result result = sc_storage_get_element_by_addr(addr, &el); - return result == SC_RESULT_OK; } @@ -158,19 +324,45 @@ sc_result sc_storage_get_element_by_addr(sc_addr addr, sc_element ** el) if (storage == null_ptr || addr.seg == 0 || addr.offset == 0 || addr.seg > storage->max_segments_count || addr.offset > SC_SEGMENT_ELEMENTS_COUNT) - goto error; + return result; + + // Проверка L1-кэша + sc_segment * segment = _sc_l1_cache_get(storage->l1_cache, addr.seg); + if (segment) + { + *el = &segment->elements[addr.offset]; + if (((*el)->flags.states & SC_STATE_ELEMENT_EXIST) != SC_STATE_ELEMENT_EXIST) + return result; + return SC_RESULT_OK; + } + + // Проверка L2-кэша + segment = _sc_l2_cache_get(storage->l2_cache, addr.seg); + if (segment) + { + sc_monitor_acquire_write(&storage->segments_monitor); + storage->segments[addr.seg - 1] = segment; + sc_monitor_release_write(&storage->segments_monitor); + _sc_l1_cache_add(storage->l1_cache, addr.seg, segment); + *el = &segment->elements[addr.offset]; + if (((*el)->flags.states & SC_STATE_ELEMENT_EXIST) != SC_STATE_ELEMENT_EXIST) + return result; + return SC_RESULT_OK; + } - sc_segment * segment = storage->segments[addr.seg - 1]; + // Проверка основного хранилища + sc_monitor_acquire_read(&storage->segments_monitor); + segment = storage->segments[addr.seg - 1]; + sc_monitor_release_read(&storage->segments_monitor); if (segment == null_ptr) - goto error; + return result; + _sc_l1_cache_add(storage->l1_cache, addr.seg, segment); *el = &segment->elements[addr.offset]; if (((*el)->flags.states & SC_STATE_ELEMENT_EXIST) != SC_STATE_ELEMENT_EXIST) - goto error; + return result; - result = SC_RESULT_OK; -error: - return result; + return SC_RESULT_OK; } sc_result sc_storage_free_element(sc_addr addr) @@ -179,15 +371,33 @@ sc_result sc_storage_free_element(sc_addr addr) sc_element * element; if (sc_storage_get_element_by_addr(addr, &element) != SC_RESULT_OK) - goto error; + return result; - sc_monitor_acquire_read(&storage->segments_monitor); - sc_segment * segment = storage->segments[addr.seg - 1]; - sc_monitor_release_read(&storage->segments_monitor); + // Проверка L1-кэша + sc_segment * segment = _sc_l1_cache_get(storage->l1_cache, addr.seg); + if (!segment) + { + // Проверка L2-кэша + segment = _sc_l2_cache_get(storage->l2_cache, addr.seg); + if (segment) + { + sc_monitor_acquire_write(&storage->segments_monitor); + storage->segments[addr.seg - 1] = segment; + sc_monitor_release_write(&storage->segments_monitor); + _sc_l1_cache_add(storage->l1_cache, addr.seg, segment); + } + } + if (!segment) + { + sc_monitor_acquire_read(&storage->segments_monitor); + segment = storage->segments[addr.seg - 1]; + sc_monitor_release_read(&storage->segments_monitor); + } if (segment == null_ptr) - goto error; + return result; sc_monitor_acquire_write(&segment->monitor); + segment->is_modified = SC_TRUE; sc_addr_offset const last_released_offset = segment->last_released_offset; segment->elements[addr.offset] = (sc_element){(sc_element_flags){.type = last_released_offset}}; segment->last_released_offset = addr.offset; @@ -201,9 +411,7 @@ sc_result sc_storage_free_element(sc_addr addr) sc_monitor_release_write(&storage->segments_monitor); } - result = SC_RESULT_OK; -error: - return result; + return SC_RESULT_OK; } sc_segment * _sc_storage_get_last_not_engaged_segment() @@ -211,6 +419,7 @@ sc_segment * _sc_storage_get_last_not_engaged_segment() sc_segment * segment = null_ptr; sc_addr_seg segment_num; + sc_monitor_acquire_write(&storage->segments_monitor); do { segment_num = storage->last_not_engaged_segment_num; @@ -221,57 +430,93 @@ sc_segment * _sc_storage_get_last_not_engaged_segment() storage->last_not_engaged_segment_num = segment->elements[0].flags.states; segment->elements[0].flags.states = 0; } + else if (segment_num != 0) + { + segment = _sc_l2_cache_get(storage->l2_cache, segment_num); + if (segment) + { + storage->segments[segment_num - 1] = segment; + _sc_l1_cache_add(storage->l1_cache, segment_num, segment); + storage->last_not_engaged_segment_num = segment->elements[0].flags.states; + segment->elements[0].flags.states = 0; + } + } } while (segment != null_ptr && (segment->last_engaged_offset + 1 == SC_SEGMENT_ELEMENTS_COUNT && segment->last_released_offset == 0)); + sc_monitor_release_write(&storage->segments_monitor); return segment; } + + sc_segment * _sc_storage_get_new_segment() { sc_segment * segment = null_ptr; + sc_monitor_acquire_write(&storage->segments_monitor); if (storage->segments_count == storage->max_segments_count) - goto error; + { + sc_monitor_release_write(&storage->segments_monitor); + return null_ptr; + } - segment = storage->segments[storage->segments_count] = sc_segment_new(storage->segments_count + 1); + segment = sc_segment_new(storage->segments_count + 1); + storage->segments[storage->segments_count] = segment; + _sc_l1_cache_add(storage->l1_cache, storage->segments_count + 1, segment); ++storage->segments_count; + sc_monitor_release_write(&storage->segments_monitor); -error: return segment; } + + sc_segment * _sc_storage_get_last_free_segment() { sc_segment * segment = null_ptr; - + sc_monitor_acquire_read(&storage->segments_monitor); if (storage->segments_count == 0) - goto error; + { + sc_monitor_release_read(&storage->segments_monitor); + return null_ptr; + } sc_addr_seg last_segment_idx = storage->segments_count - 1; segment = storage->segments[last_segment_idx]; + sc_monitor_release_read(&storage->segments_monitor); - if (segment->last_engaged_offset + 1 == SC_SEGMENT_ELEMENTS_COUNT) + if (segment == null_ptr) { - segment = null_ptr; - goto error; + segment = _sc_l2_cache_get(storage->l2_cache, last_segment_idx + 1); + if (segment) + { + sc_monitor_acquire_write(&storage->segments_monitor); + storage->segments[last_segment_idx] = segment; + sc_monitor_release_write(&storage->segments_monitor); + _sc_l1_cache_add(storage->l1_cache, last_segment_idx + 1, segment); + } } -error: + if (segment && segment->last_engaged_offset + 1 == SC_SEGMENT_ELEMENTS_COUNT) + segment = null_ptr; + return segment; } + + void _sc_storage_check_segment_type(sc_segment ** segment) { - sc_monitor_acquire_read(&(*segment)->monitor); - sc_addr_offset last_released_offset = (*segment)->last_released_offset; - sc_addr_offset last_engaged_offset = (*segment)->last_engaged_offset; - sc_monitor_release_read(&(*segment)->monitor); - - if (last_released_offset != 0) - return; - else if (last_engaged_offset + 1 == SC_SEGMENT_ELEMENTS_COUNT) - *segment = null_ptr; + sc_monitor_acquire_read(&(*segment)->monitor); + sc_addr_offset last_released_offset = (*segment)->last_released_offset; + sc_addr_offset last_engaged_offset = (*segment)->last_engaged_offset; + sc_monitor_release_read(&(*segment)->monitor); + + if (last_released_offset != 0) + return; + else if (last_engaged_offset + 1 == SC_SEGMENT_ELEMENTS_COUNT) + *segment = null_ptr; } sc_segment * _sc_storage_get_segment() @@ -280,10 +525,8 @@ sc_segment * _sc_storage_get_segment() sc_thread * thread = sc_thread_self(); sc_monitor_acquire_read(&storage->processes_monitor); - if (storage->processes_segments_table != null_ptr) segment = (sc_segment *)sc_hash_table_get(storage->processes_segments_table, thread); - sc_monitor_release_read(&storage->processes_monitor); if (segment != null_ptr) @@ -322,15 +565,15 @@ sc_element * _sc_storage_get_element(sc_addr * addr) sc_segment * segment = _sc_storage_get_segment(); if (segment == null_ptr) - goto error; + return null_ptr; sc_monitor_acquire_write(&segment->monitor); + segment->is_modified = SC_TRUE; if (segment->last_engaged_offset + 1 != SC_SEGMENT_ELEMENTS_COUNT) { element_offset = ++segment->last_engaged_offset; element = &segment->elements[element_offset]; - *addr = (sc_addr){segment->num, element_offset}; } else if (segment->last_released_offset != 0) @@ -339,13 +582,10 @@ sc_element * _sc_storage_get_element(sc_addr * addr) element = &segment->elements[element_offset]; segment->last_released_offset = element->flags.type; element->flags.type = 0; - *addr = (sc_addr){segment->num, element_offset}; } sc_monitor_release_write(&segment->monitor); - -error: return element; } @@ -366,12 +606,30 @@ sc_element * _sc_storage_get_released_element(sc_addr * addr) } segment = storage->segments[segment_num - 1]; + if (segment == null_ptr) + { + segment = _sc_l2_cache_get(storage->l2_cache, segment_num); + if (segment) + { + storage->segments[segment_num - 1] = segment; + _sc_l1_cache_add(storage->l1_cache, segment_num, segment); + } + } + + if (segment == null_ptr) + { + storage->last_released_segment_num = 0; // Сбрасываем, если сегмент не найден + goto error; + } + sc_monitor_acquire_write(&segment->monitor); + segment->is_modified = SC_TRUE; element_offset = segment->last_released_offset; if (segment->last_released_offset == 0) { storage->last_released_segment_num = segment->elements[0].flags.type; segment->elements[0].flags.type = 0; + sc_monitor_release_write(&segment->monitor); goto new_segment; } else @@ -380,6 +638,7 @@ sc_element * _sc_storage_get_released_element(sc_addr * addr) segment->last_released_offset = element->flags.type; element->flags.type = 0; } + sc_monitor_release_write(&segment->monitor); if (segment->last_released_offset == 0) { @@ -389,12 +648,12 @@ sc_element * _sc_storage_get_released_element(sc_addr * addr) error: sc_monitor_release_write(&storage->segments_monitor); - *addr = (sc_addr){segment_num, element_offset}; - return element; } + + sc_element * sc_storage_allocate_new_element(sc_memory_context const * ctx, sc_addr * addr) { *addr = SC_ADDR_EMPTY; @@ -406,7 +665,7 @@ sc_element * sc_storage_allocate_new_element(sc_memory_context const * ctx, sc_a element = _sc_storage_get_released_element(addr); if (element == null_ptr) sc_memory_error( - "Max segments count is %d. SC-memory is full. Please, extends or swap sc-memory", + "Max segments count is %d. SC-memory is full. Please, extend or swap sc-memory", storage->max_segments_count); } @@ -418,1188 +677,1265 @@ sc_element * sc_storage_allocate_new_element(sc_memory_context const * ctx, sc_a void sc_storage_start_new_process() { - if (storage == null_ptr) - return; + if (storage == null_ptr) + return; - sc_thread * thread = sc_thread_self(); - sc_monitor_acquire_write(&storage->processes_monitor); - if (storage->processes_segments_table == null_ptr) - goto end; + sc_thread * thread = sc_thread_self(); + sc_monitor_acquire_write(&storage->processes_monitor); + if (storage->processes_segments_table == null_ptr) + goto end; - sc_hash_table_insert(storage->processes_segments_table, thread, null_ptr); + sc_hash_table_insert(storage->processes_segments_table, thread, null_ptr); end: - sc_monitor_release_write(&storage->processes_monitor); + sc_monitor_release_write(&storage->processes_monitor); } void sc_storage_end_new_process() { - if (storage == null_ptr) - return; - - sc_thread * thread = sc_thread_self(); - sc_monitor_acquire_write(&storage->processes_monitor); - if (storage->processes_segments_table == null_ptr) - goto end; - - sc_segment * segment = sc_hash_table_get(storage->processes_segments_table, thread); - if (segment != null_ptr - && (segment->last_engaged_offset + 1 != SC_SEGMENT_ELEMENTS_COUNT || segment->last_released_offset != 0)) - { - sc_monitor_acquire_write(&storage->segments_monitor); - - sc_addr_seg const last_not_engaged_segment_num = storage->last_not_engaged_segment_num; - segment->elements[0].flags.states = last_not_engaged_segment_num; - storage->last_not_engaged_segment_num = segment->num; - - sc_monitor_release_write(&storage->segments_monitor); - } - sc_hash_table_insert(storage->processes_segments_table, thread, null_ptr); + if (storage == null_ptr) + return; + + sc_thread * thread = sc_thread_self(); + sc_monitor_acquire_write(&storage->processes_monitor); + if (storage->processes_segments_table == null_ptr) + goto end; + + sc_segment * segment = sc_hash_table_get(storage->processes_segments_table, thread); + if (segment != null_ptr + && (segment->last_engaged_offset + 1 != SC_SEGMENT_ELEMENTS_COUNT || segment->last_released_offset != 0)) + { + sc_monitor_acquire_write(&storage->segments_monitor); + sc_addr_seg const last_not_engaged_segment_num = storage->last_not_engaged_segment_num; + segment->elements[0].flags.states = last_not_engaged_segment_num; + storage->last_not_engaged_segment_num = segment->num; + sc_monitor_release_write(&storage->segments_monitor); + } + sc_hash_table_insert(storage->processes_segments_table, thread, null_ptr); end: - sc_monitor_release_write(&storage->processes_monitor); + sc_monitor_release_write(&storage->processes_monitor); } sc_result _sc_storage_element_erase(sc_addr addr) { - sc_result result; - - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_write(monitor); + sc_result result; - sc_element * element; - result = sc_storage_get_element_by_addr(addr, &element); - if (result != SC_RESULT_OK || (element->flags.states & SC_STATE_REQUEST_ERASURE) == SC_STATE_REQUEST_ERASURE) - { - sc_monitor_release_write(monitor); - return result; - } + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_write(monitor); - element->flags.states |= SC_STATE_REQUEST_ERASURE; - sc_type type = element->flags.type; + sc_element * element; + result = sc_storage_get_element_by_addr(addr, &element); + if (result != SC_RESULT_OK || (element->flags.states & SC_STATE_REQUEST_ERASURE) == SC_STATE_REQUEST_ERASURE) + { + sc_monitor_release_write(monitor); + return result; + } - sc_monitor_release_write(monitor); + element->flags.states |= SC_STATE_REQUEST_ERASURE; + sc_type type = element->flags.type; - if (sc_type_has_subtype(type, sc_type_node_link)) - sc_fs_memory_unlink_string(SC_ADDR_LOCAL_TO_INT(addr)); - else if (sc_type_has_subtype_in_mask(type, sc_type_connector_mask)) - { - sc_bool const is_edge = sc_type_has_subtype(type, sc_type_common_edge); - - sc_addr begin_addr = element->arc.begin; - sc_addr end_addr = element->arc.end; - - sc_bool const is_not_loop = SC_ADDR_IS_NOT_EQUAL(begin_addr, end_addr); - - sc_monitor * beg_monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, begin_addr); - sc_monitor * end_monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, end_addr); - - sc_monitor_acquire_write_n(2, beg_monitor, end_monitor); - - // outgoing sc-arcs - sc_addr prev_out_connector_addr = element->arc.prev_begin_out_arc; - sc_monitor * prev_out_arc_monitor = null_ptr; - if (SC_ADDR_IS_NOT_EQUAL(begin_addr, prev_out_connector_addr) - && SC_ADDR_IS_NOT_EQUAL(end_addr, prev_out_connector_addr)) - prev_out_arc_monitor = - sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, prev_out_connector_addr); - - sc_addr next_out_connector_addr = element->arc.next_begin_out_arc; - sc_monitor * next_out_arc_monitor = null_ptr; - if (SC_ADDR_IS_NOT_EQUAL(begin_addr, next_out_connector_addr) - && SC_ADDR_IS_NOT_EQUAL(end_addr, next_out_connector_addr)) - next_out_arc_monitor = - sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, next_out_connector_addr); - - // incoming sc-arcs - sc_addr prev_in_connector_addr = element->arc.prev_end_in_arc; - sc_monitor * prev_in_arc_monitor = null_ptr; - if (SC_ADDR_IS_NOT_EQUAL(begin_addr, prev_in_connector_addr) - && SC_ADDR_IS_NOT_EQUAL(end_addr, prev_in_connector_addr)) - prev_in_arc_monitor = - sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, prev_in_connector_addr); - - sc_addr next_in_arc = element->arc.next_end_in_arc; - sc_monitor * next_in_arc_monitor = null_ptr; - if (SC_ADDR_IS_NOT_EQUAL(begin_addr, next_in_arc) && SC_ADDR_IS_NOT_EQUAL(end_addr, next_in_arc)) - next_in_arc_monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, next_in_arc); + sc_monitor_release_write(monitor); -#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES - sc_addr prev_in_arc_from_structure = element->arc.prev_in_arc_from_structure; - sc_monitor * prev_in_arc_from_structure_monitor = null_ptr; - if (SC_ADDR_IS_NOT_EQUAL(begin_addr, prev_in_arc_from_structure) - && SC_ADDR_IS_NOT_EQUAL(end_addr, prev_in_arc_from_structure)) - prev_in_arc_from_structure_monitor = - sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, prev_in_arc_from_structure); - - sc_addr next_in_arc_from_structure_addr = element->arc.next_in_arc_from_structure; - sc_monitor * next_in_arc_from_structure_monitor = null_ptr; - if (SC_ADDR_IS_NOT_EQUAL(begin_addr, next_in_arc_from_structure_addr) - && SC_ADDR_IS_NOT_EQUAL(end_addr, next_in_arc_from_structure_addr)) - next_in_arc_from_structure_monitor = - sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, next_in_arc_from_structure_addr); -#endif + // Добавляем операцию удаления в буфер + if (!_sc_storage_add_operation(SC_OP_ERASE_ELEMENT, addr, 0, SC_ADDR_EMPTY, SC_ADDR_EMPTY, null_ptr, 0)) + { + return SC_RESULT_ERROR_FULL_MEMORY; + } -#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES - sc_monitor_acquire_write_n( - 6, - prev_out_arc_monitor, - next_out_arc_monitor, - prev_in_arc_monitor, - next_in_arc_monitor, - prev_in_arc_from_structure_monitor, - next_in_arc_from_structure_monitor); -#else - sc_monitor_acquire_write_n(3, prev_out_arc_monitor, next_out_arc_monitor, prev_in_arc_monitor, next_in_arc_monitor); -#endif + // Уведомляем о событии удаления + sc_event_notify_element_deleted(addr); - if (SC_ADDR_IS_NOT_EMPTY(prev_out_connector_addr)) - { - sc_element * prev_el_arc; - result = sc_storage_get_element_by_addr(prev_out_connector_addr, &prev_el_arc); - if (result == SC_RESULT_OK) - prev_el_arc->arc.next_begin_out_arc = next_out_connector_addr; - } + return SC_RESULT_OK; +} - if (SC_ADDR_IS_NOT_EMPTY(next_out_connector_addr)) - { - sc_element * next_el_arc; - result = sc_storage_get_element_by_addr(next_out_connector_addr, &next_el_arc); - if (result == SC_RESULT_OK) - next_el_arc->arc.prev_begin_out_arc = prev_out_connector_addr; - } +sc_result sc_storage_element_erase(sc_memory_context const * ctx, sc_addr addr) +{ + sc_result result; + + sc_element * el = null_ptr; + result = sc_storage_get_element_by_addr(addr, &el); + if (result != SC_RESULT_OK) + goto error; + + sc_hash_table * cache_table = sc_hash_table_init(g_direct_hash, g_direct_equal, null_ptr, null_ptr); + sc_queue iter_queue; + sc_queue_init(&iter_queue); + sc_pointer p_addr = GUINT_TO_POINTER(SC_ADDR_LOCAL_TO_INT(addr)); + sc_queue_push(&iter_queue, p_addr); + + sc_queue addrs_with_not_emitted_erase_events; + sc_queue_init(&addrs_with_not_emitted_erase_events); + while (!sc_queue_empty(&iter_queue)) + { + p_addr = sc_queue_pop(&iter_queue); + sc_addr element_addr; + element_addr.seg = SC_ADDR_LOCAL_SEG_FROM_INT((sc_pointer_to_sc_addr_hash)p_addr); + element_addr.offset = SC_ADDR_LOCAL_OFFSET_FROM_INT((sc_pointer_to_sc_addr_hash)p_addr); + + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, element_addr); + sc_monitor_acquire_read(monitor); + result = sc_storage_get_element_by_addr(element_addr, &el); + if (result != SC_RESULT_OK) + { + sc_monitor_release_read(monitor); + continue; + } + + sc_type const type = el->flags.type; + sc_addr const begin_addr = el->arc.begin; + sc_addr const end_addr = el->arc.end; + + sc_result erase_incoming_connector_result = SC_RESULT_NO; + sc_result erase_outgoing_connector_result = SC_RESULT_NO; + sc_result erase_incoming_arc_result = SC_RESULT_NO; + sc_result erase_outgoing_arc_result = SC_RESULT_NO; + sc_result erase_element_result = SC_RESULT_NO; + + if ((el->flags.states & SC_STATE_IS_ERASABLE) != SC_STATE_IS_ERASABLE) + { + if ((type & sc_type_connector_mask) != 0) + { + erase_incoming_connector_result = sc_event_emit( + ctx, + begin_addr, + sc_event_before_erase_connector_addr, + element_addr, + type, + end_addr, + sc_storage_element_erase, + element_addr); + erase_outgoing_connector_result = sc_event_emit( + ctx, + end_addr, + sc_event_before_erase_connector_addr, + element_addr, + type, + begin_addr, + sc_storage_element_erase, + element_addr); + } + + if (sc_type_has_subtype(type, sc_type_common_edge)) + { + erase_incoming_arc_result = sc_event_emit( + ctx, + begin_addr, + sc_event_before_erase_edge_addr, + element_addr, + type, + end_addr, + sc_storage_element_erase, + element_addr); + erase_outgoing_arc_result = sc_event_emit( + ctx, + end_addr, + sc_event_before_erase_edge_addr, + element_addr, + type, + begin_addr, + sc_storage_element_erase, + element_addr); + } + else if (sc_type_has_subtype_in_mask(type, sc_type_arc_mask)) + { + erase_outgoing_arc_result = sc_event_emit( + ctx, + begin_addr, + sc_event_before_erase_outgoing_arc_addr, + element_addr, + type, + end_addr, + sc_storage_element_erase, + element_addr); + erase_incoming_arc_result = sc_event_emit( + ctx, + end_addr, + sc_event_before_erase_incoming_arc_addr, + element_addr, + type, + begin_addr, + sc_storage_element_erase, + element_addr); + } + + erase_element_result = sc_event_emit( + ctx, + element_addr, + sc_event_before_erase_element_addr, + SC_ADDR_EMPTY, + 0, + SC_ADDR_EMPTY, + sc_storage_element_erase, + element_addr); + + el->flags.states |= SC_STATE_IS_ERASABLE; + } + + if (erase_incoming_connector_result == SC_RESULT_OK || erase_outgoing_connector_result == SC_RESULT_OK + || erase_incoming_arc_result == SC_RESULT_OK || erase_outgoing_arc_result == SC_RESULT_OK + || erase_element_result == SC_RESULT_OK) + { + sc_monitor_release_read(monitor); + continue; + } + + sc_queue_push(&addrs_with_not_emitted_erase_events, p_addr); + + sc_addr connector_addr = el->first_out_arc; + while (SC_ADDR_IS_NOT_EMPTY(connector_addr)) + { + p_addr = GUINT_TO_POINTER(SC_ADDR_LOCAL_TO_INT(connector_addr)); + sc_element * connector = sc_hash_table_get(cache_table, p_addr); + if (connector == null_ptr) + { + result = sc_storage_get_element_by_addr(connector_addr, &connector); + if (result != SC_RESULT_OK) + break; + sc_hash_table_insert(cache_table, p_addr, connector); + sc_queue_push(&iter_queue, p_addr); + } + connector_addr = connector->arc.next_begin_out_arc; + } + + connector_addr = el->first_in_arc; + while (SC_ADDR_IS_NOT_EMPTY(connector_addr)) + { + p_addr = GUINT_TO_POINTER(SC_ADDR_LOCAL_TO_INT(connector_addr)); + sc_element * connector = sc_hash_table_get(cache_table, p_addr); + if (connector == null_ptr) + { + result = sc_storage_get_element_by_addr(connector_addr, &connector); + if (result != SC_RESULT_OK) + break; + sc_hash_table_insert(cache_table, p_addr, connector); + sc_queue_push(&iter_queue, p_addr); + } + connector_addr = connector->arc.next_end_in_arc; + } + + sc_monitor_release_read(monitor); + } + + sc_queue_destroy(&iter_queue); + sc_hash_table_destroy(cache_table); + + while (!sc_queue_empty(&addrs_with_not_emitted_erase_events)) + { + sc_addr_hash addr_int = (sc_pointer_to_sc_addr_hash)sc_queue_pop(&addrs_with_not_emitted_erase_events); + addr.seg = SC_ADDR_LOCAL_SEG_FROM_INT(addr_int); + addr.offset = SC_ADDR_LOCAL_OFFSET_FROM_INT(addr_int); + _sc_storage_element_erase(addr); + } + + sc_queue_destroy(&addrs_with_not_emitted_erase_events); + result = SC_RESULT_OK; - sc_element * b_el; - result = sc_storage_get_element_by_addr(begin_addr, &b_el); - if (result == SC_RESULT_OK) - { - if (SC_ADDR_IS_EQUAL(addr, b_el->first_out_arc)) - b_el->first_out_arc = next_out_connector_addr; +error: + return result; +} - --b_el->outgoing_arcs_count; +sc_addr sc_storage_node_new(sc_memory_context const * ctx, sc_type type) +{ + sc_result result; + return sc_storage_node_new_ext(ctx, type, &result); +} - if (is_edge && is_not_loop) - { - if (SC_ADDR_IS_EQUAL(addr, b_el->first_in_arc)) - b_el->first_in_arc = next_in_arc; +sc_addr sc_storage_node_new_ext(sc_memory_context const * ctx, sc_type type, sc_result * result) +{ + sc_addr addr = SC_ADDR_EMPTY; + + if (sc_type_is_not_node(type) && (!sc_type_is(type, sc_type_const) && !sc_type_is(type, sc_type_var))) + { + *result = SC_RESULT_ERROR_ELEMENT_IS_NOT_NODE; + return addr; + } + + sc_element * element = sc_storage_allocate_new_element(ctx, &addr); + if (element == null_ptr) + { + *result = SC_RESULT_ERROR_FULL_MEMORY; + return addr; + } + + // Добавляем операцию создания узла в буфер + if (!_sc_storage_add_operation(SC_OP_NODE_NEW, addr, sc_type_node | type, SC_ADDR_EMPTY, SC_ADDR_EMPTY, null_ptr, 0)) + { + sc_storage_free_element(addr); + *result = SC_RESULT_ERROR_FULL_MEMORY; + return addr; + } + + *result = SC_RESULT_OK; + return addr; +} - --b_el->incoming_arcs_count; - } - } +sc_addr sc_storage_link_new(sc_memory_context const * ctx, sc_type type) +{ + sc_result result; + return sc_storage_link_new_ext(ctx, type, &result); +} - if (SC_ADDR_IS_NOT_EMPTY(prev_in_connector_addr)) - { - sc_element * prev_el_arc; - result = sc_storage_get_element_by_addr(prev_in_connector_addr, &prev_el_arc); - if (result == SC_RESULT_OK) - prev_el_arc->arc.next_end_in_arc = next_in_arc; - } +sc_addr sc_storage_link_new_ext(sc_memory_context const * ctx, sc_type type, sc_result * result) +{ + sc_addr addr = SC_ADDR_EMPTY; + + if (sc_type_is_not_node_link(type)) + { + *result = SC_RESULT_ERROR_ELEMENT_IS_NOT_LINK; + return addr; + } + + sc_element * element = sc_storage_allocate_new_element(ctx, &addr); + if (element == null_ptr) + { + *result = SC_RESULT_ERROR_FULL_MEMORY; + return addr; + } + + // Добавляем операцию создания ссылки в буфер + if (!_sc_storage_add_operation(SC_OP_LINK_NEW, addr, sc_type_node_link | type, SC_ADDR_EMPTY, SC_ADDR_EMPTY, null_ptr, 0)) + { + sc_storage_free_element(addr); + *result = SC_RESULT_ERROR_FULL_MEMORY; + return addr; + } + + *result = SC_RESULT_OK; + return addr; +} - if (SC_ADDR_IS_NOT_EMPTY(next_in_arc)) - { - sc_element * next_el_arc; - result = sc_storage_get_element_by_addr(next_in_arc, &next_el_arc); - if (result == SC_RESULT_OK) - next_el_arc->arc.prev_end_in_arc = prev_in_connector_addr; - } +void _sc_storage_make_elements_incident_to_arc( + sc_addr connector_addr, + sc_element * arc_el, + sc_addr beg_addr, + sc_element * beg_el, + sc_addr end_addr, + sc_element * end_el, + sc_bool is_reverse, + sc_bool is_loop) +{ + sc_element *first_out_arc = null_ptr, *first_in_arc = null_ptr; + sc_addr first_out_connector_addr = beg_el->first_out_arc; + sc_addr first_in_connector_addr = end_el->first_in_arc; + sc_monitor * first_out_arc_monitor = null_ptr; + sc_monitor * first_in_arc_monitor = null_ptr; + + if (SC_ADDR_IS_NOT_EQUAL(first_out_connector_addr, beg_addr) + && SC_ADDR_IS_NOT_EQUAL(first_out_connector_addr, end_addr)) + first_out_arc_monitor = + sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, first_out_connector_addr); + if (SC_ADDR_IS_NOT_EQUAL(first_in_connector_addr, beg_addr) + && SC_ADDR_IS_NOT_EQUAL(first_in_connector_addr, end_addr)) + first_in_arc_monitor = + sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, first_in_connector_addr); + + sc_monitor_acquire_write_n(2, first_out_arc_monitor, first_in_arc_monitor); + + if (SC_ADDR_IS_NOT_EMPTY(first_out_connector_addr)) + sc_storage_get_element_by_addr(first_out_connector_addr, &first_out_arc); + if (SC_ADDR_IS_NOT_EMPTY(first_in_connector_addr)) + sc_storage_get_element_by_addr(first_in_connector_addr, &first_in_arc); + + if (is_reverse) + { + arc_el->arc.next_end_out_arc = first_out_connector_addr; + arc_el->arc.next_begin_in_arc = first_in_connector_addr; + } + else + { + arc_el->arc.next_begin_out_arc = first_out_connector_addr; + arc_el->arc.next_end_in_arc = first_in_connector_addr; + if (is_loop) + { + arc_el->arc.next_end_out_arc = first_out_connector_addr; + arc_el->arc.next_begin_in_arc = first_in_connector_addr; + } + if (first_out_arc) + first_out_arc->arc.prev_begin_out_arc = connector_addr; + if (first_in_arc) + first_in_arc->arc.prev_end_in_arc = connector_addr; + } + + sc_monitor_release_write_n(2, first_out_arc_monitor, first_in_arc_monitor); + beg_el->first_out_arc = connector_addr; + end_el->first_in_arc = connector_addr; + ++beg_el->outgoing_arcs_count; + ++end_el->incoming_arcs_count; +} #ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES - if (SC_ADDR_IS_NOT_EMPTY(prev_in_arc_from_structure)) - { - sc_element * prev_el_arc; - result = sc_storage_get_element_by_addr(prev_in_arc_from_structure, &prev_el_arc); - if (result == SC_RESULT_OK) - prev_el_arc->arc.next_in_arc_from_structure = next_in_arc_from_structure_addr; - } +void _sc_storage_update_structure_arcs( + sc_addr connector_addr, + sc_element * arc_el, + sc_addr beg_addr, + sc_addr end_addr, + sc_element * end_el) +{ + sc_element * first_in_accessed_arc = null_ptr; + sc_addr first_in_accessed_connector_addr = end_el->first_in_arc_from_structure; + sc_monitor * first_in_accessed_arc_monitor = null_ptr; - if (SC_ADDR_IS_NOT_EMPTY(next_in_arc_from_structure_addr)) - { - sc_element * next_el_arc; - result = sc_storage_get_element_by_addr(next_in_arc_from_structure_addr, &next_el_arc); - if (result == SC_RESULT_OK) - next_el_arc->arc.prev_in_arc_from_structure = prev_in_arc_from_structure; - } -#endif + if (SC_ADDR_IS_NOT_EQUAL(first_in_accessed_connector_addr, beg_addr) + && SC_ADDR_IS_NOT_EQUAL(first_in_accessed_connector_addr, end_addr)) + first_in_accessed_arc_monitor = + sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, first_in_accessed_connector_addr); - sc_element * e_el; - result = sc_storage_get_element_by_addr(end_addr, &e_el); - if (result == SC_RESULT_OK) - { - if (SC_ADDR_IS_EQUAL(addr, e_el->first_in_arc)) - e_el->first_in_arc = next_in_arc; + sc_monitor_acquire_write(first_in_accessed_arc_monitor); -#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES - if (SC_ADDR_IS_EQUAL(addr, e_el->first_in_arc_from_structure)) - e_el->first_in_arc_from_structure = next_in_arc_from_structure_addr; -#endif + if (SC_ADDR_IS_NOT_EMPTY(first_in_accessed_connector_addr)) + sc_storage_get_element_by_addr(first_in_accessed_connector_addr, &first_in_accessed_arc); - --e_el->incoming_arcs_count; + arc_el->arc.next_in_arc_from_structure = first_in_accessed_connector_addr; + if (first_in_accessed_arc) + first_in_accessed_arc->arc.prev_in_arc_from_structure = connector_addr; - if (is_edge && is_not_loop) - { - if (SC_ADDR_IS_EQUAL(addr, e_el->first_out_arc)) - e_el->first_out_arc = next_out_connector_addr; + sc_monitor_release_write(first_in_accessed_arc_monitor); + end_el->first_in_arc_from_structure = connector_addr; +} +#endif - --e_el->outgoing_arcs_count; - } - } +sc_addr sc_storage_arc_new(sc_memory_context const * ctx, sc_type type, sc_addr beg_addr, sc_addr end_addr) +{ + sc_result result; + return sc_storage_arc_new_ext(ctx, type, beg_addr, end_addr, &result); +} + +sc_addr sc_storage_arc_new_ext( + sc_memory_context const * ctx, + sc_type type, + sc_addr beg_addr, + sc_addr end_addr, + sc_result * result) +{ + sc_addr connector_addr = SC_ADDR_EMPTY; + + if (sc_type_is_not_connector(type)) + { + *result = SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR; + return connector_addr; + } + + if (SC_ADDR_IS_EMPTY(beg_addr) || SC_ADDR_IS_EMPTY(end_addr)) + { + *result = SC_RESULT_ERROR_ADDR_IS_NOT_VALID; + return connector_addr; + } + + sc_element *beg_el = null_ptr, *end_el = null_ptr; + sc_element * arc_el = sc_storage_allocate_new_element(ctx, &connector_addr); + if (arc_el == null_ptr) + { + *result = SC_RESULT_ERROR_FULL_MEMORY; + return connector_addr; + } + + // Добавляем операцию создания дуги в буфер + if (!_sc_storage_add_operation(SC_OP_ARC_NEW, connector_addr, type, beg_addr, end_addr, null_ptr, 0)) + { + sc_storage_free_element(connector_addr); + *result = SC_RESULT_ERROR_FULL_MEMORY; + return connector_addr; + } + + sc_bool is_edge = sc_type_has_subtype(type, sc_type_common_edge); + sc_bool is_not_loop = SC_ADDR_IS_NOT_EQUAL(beg_addr, end_addr); + + sc_monitor * beg_monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, beg_addr); + sc_monitor * end_monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, end_addr); + sc_monitor_acquire_write_n(2, beg_monitor, end_monitor); + + *result = sc_storage_get_element_by_addr(beg_addr, &beg_el); + if (*result != SC_RESULT_OK) + goto error; + + *result = sc_storage_get_element_by_addr(end_addr, &end_el); + if (*result != SC_RESULT_OK) + goto error; + + _sc_storage_make_elements_incident_to_arc( + connector_addr, arc_el, beg_addr, beg_el, end_addr, end_el, SC_FALSE, !is_not_loop); + if (is_edge && is_not_loop) + _sc_storage_make_elements_incident_to_arc( + connector_addr, arc_el, end_addr, end_el, beg_addr, beg_el, SC_TRUE, SC_FALSE); #ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES - sc_monitor_release_write_n( - 6, - prev_out_arc_monitor, - next_out_arc_monitor, - prev_in_arc_monitor, - next_in_arc_monitor, - prev_in_arc_from_structure_monitor, - next_in_arc_from_structure_monitor); -#else - sc_monitor_release_write_n(4, prev_out_arc_monitor, next_out_arc_monitor, prev_in_arc_monitor, next_in_arc_monitor); + if (sc_type_is_structure_and_arc(beg_el->flags.type, type)) + _sc_storage_update_structure_arcs(connector_addr, arc_el, beg_addr, end_addr, end_el); #endif - sc_monitor_release_write_n(2, beg_monitor, end_monitor); - } - - sc_monitor_acquire_write(monitor); - sc_storage_free_element(addr); - sc_monitor_release_write(monitor); - // erase registered events before deletion - sc_event_notify_element_deleted(addr); + if (is_edge && is_not_loop) + { + sc_event_emit( + ctx, end_addr, sc_event_after_generate_edge_addr, connector_addr, type, beg_addr, null_ptr, SC_ADDR_EMPTY); + sc_event_emit( + ctx, beg_addr, sc_event_after_generate_edge_addr, connector_addr, type, end_addr, null_ptr, SC_ADDR_EMPTY); + } + else + { + sc_event_emit( + ctx, + beg_addr, + sc_event_after_generate_outgoing_arc_addr, + connector_addr, + type, + end_addr, + null_ptr, + SC_ADDR_EMPTY); + sc_event_emit( + ctx, + end_addr, + sc_event_after_generate_incoming_arc_addr, + connector_addr, + type, + beg_addr, + null_ptr, + SC_ADDR_EMPTY); + } + + sc_event_emit( + ctx, end_addr, sc_event_after_generate_connector_addr, connector_addr, type, beg_addr, null_ptr, SC_ADDR_EMPTY); + sc_event_emit( + ctx, beg_addr, sc_event_after_generate_connector_addr, connector_addr, type, end_addr, null_ptr, SC_ADDR_EMPTY); + + sc_monitor_release_write_n(2, beg_monitor, end_monitor); + *result = SC_RESULT_OK; + return connector_addr; - return result; +error: + sc_storage_free_element(connector_addr); + sc_monitor_release_write_n(2, beg_monitor, end_monitor); + return SC_ADDR_EMPTY; } -sc_result sc_storage_element_erase(sc_memory_context const * ctx, sc_addr addr) +sc_uint32 sc_storage_get_element_outgoing_arcs_count(sc_memory_context const * ctx, sc_addr addr, sc_result * result) { - sc_result result; + sc_uint32 count = 0; + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_read(monitor); - sc_element * el = null_ptr; - result = sc_storage_get_element_by_addr(addr, &el); - if (result != SC_RESULT_OK) - goto error; + sc_element * el = null_ptr; + *result = sc_storage_get_element_by_addr(addr, &el); + if (*result != SC_RESULT_OK) + goto error; - sc_hash_table * cache_table = sc_hash_table_init(g_direct_hash, g_direct_equal, null_ptr, null_ptr); + count = el->outgoing_arcs_count; - sc_queue iter_queue; - sc_queue_init(&iter_queue); - sc_pointer p_addr = GUINT_TO_POINTER(SC_ADDR_LOCAL_TO_INT(addr)); - sc_queue_push(&iter_queue, p_addr); +error: + sc_monitor_release_read(monitor); + return count; +} - sc_queue addrs_with_not_emitted_erase_events; - sc_queue_init(&addrs_with_not_emitted_erase_events); - while (!sc_queue_empty(&iter_queue)) - { - p_addr = sc_queue_pop(&iter_queue); +sc_uint32 sc_storage_get_element_incoming_arcs_count(sc_memory_context const * ctx, sc_addr addr, sc_result * result) +{ + sc_uint32 count = 0; + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_read(monitor); - sc_addr element_addr; - element_addr.seg = SC_ADDR_LOCAL_SEG_FROM_INT((sc_pointer_to_sc_addr_hash)p_addr); - element_addr.offset = SC_ADDR_LOCAL_OFFSET_FROM_INT((sc_pointer_to_sc_addr_hash)p_addr); + sc_element * el = null_ptr; + *result = sc_storage_get_element_by_addr(addr, &el); + if (*result != SC_RESULT_OK) + goto error; - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, element_addr); - sc_monitor_acquire_read(monitor); - result = sc_storage_get_element_by_addr(element_addr, &el); - if (result != SC_RESULT_OK) - { - sc_monitor_release_read(monitor); - continue; - } + count = el->incoming_arcs_count; - sc_type const type = el->flags.type; - sc_addr const begin_addr = el->arc.begin; - sc_addr const end_addr = el->arc.end; - - sc_result erase_incoming_connector_result = SC_RESULT_NO; - sc_result erase_outgoing_connector_result = SC_RESULT_NO; - sc_result erase_incoming_arc_result = SC_RESULT_NO; - sc_result erase_outgoing_arc_result = SC_RESULT_NO; - sc_result erase_element_result = SC_RESULT_NO; - - if ((el->flags.states & SC_STATE_IS_ERASABLE) != SC_STATE_IS_ERASABLE) - { - if ((type & sc_type_connector_mask) != 0) - { - erase_incoming_connector_result = sc_event_emit( - ctx, - begin_addr, - sc_event_before_erase_connector_addr, - element_addr, - type, - end_addr, - sc_storage_element_erase, - element_addr); - erase_outgoing_connector_result = sc_event_emit( - ctx, - end_addr, - sc_event_before_erase_connector_addr, - element_addr, - type, - begin_addr, - sc_storage_element_erase, - element_addr); - } - - if (sc_type_has_subtype(type, sc_type_common_edge)) - { - erase_incoming_arc_result = sc_event_emit( - ctx, - begin_addr, - sc_event_before_erase_edge_addr, - element_addr, - type, - end_addr, - sc_storage_element_erase, - element_addr); - erase_outgoing_arc_result = sc_event_emit( - ctx, - end_addr, - sc_event_before_erase_edge_addr, - element_addr, - type, - begin_addr, - sc_storage_element_erase, - element_addr); - } - else if (sc_type_has_subtype_in_mask(type, sc_type_arc_mask)) - { - erase_outgoing_arc_result = sc_event_emit( - ctx, - begin_addr, - sc_event_before_erase_outgoing_arc_addr, - element_addr, - type, - end_addr, - sc_storage_element_erase, - element_addr); - erase_incoming_arc_result = sc_event_emit( - ctx, - end_addr, - sc_event_before_erase_incoming_arc_addr, - element_addr, - type, - begin_addr, - sc_storage_element_erase, - element_addr); - } - - erase_element_result = sc_event_emit( - ctx, - element_addr, - sc_event_before_erase_element_addr, - SC_ADDR_EMPTY, - 0, - SC_ADDR_EMPTY, - sc_storage_element_erase, - element_addr); - - el->flags.states |= SC_STATE_IS_ERASABLE; - } - - if (erase_incoming_connector_result == SC_RESULT_OK || erase_outgoing_connector_result == SC_RESULT_OK - || erase_incoming_arc_result == SC_RESULT_OK || erase_outgoing_arc_result == SC_RESULT_OK - || erase_element_result == SC_RESULT_OK) - { - sc_monitor_release_read(monitor); - continue; - } - - sc_queue_push(&addrs_with_not_emitted_erase_events, p_addr); - - sc_addr connector_addr = el->first_out_arc; - while (SC_ADDR_IS_NOT_EMPTY(connector_addr)) - { - p_addr = GUINT_TO_POINTER(SC_ADDR_LOCAL_TO_INT(connector_addr)); - - sc_element * connector = sc_hash_table_get(cache_table, p_addr); - if (connector == null_ptr) - { - result = sc_storage_get_element_by_addr(connector_addr, &connector); - if (result != SC_RESULT_OK) - break; - - sc_hash_table_insert(cache_table, p_addr, connector); - sc_queue_push(&iter_queue, p_addr); - } - - connector_addr = connector->arc.next_begin_out_arc; - } - - connector_addr = el->first_in_arc; - while (SC_ADDR_IS_NOT_EMPTY(connector_addr)) - { - p_addr = GUINT_TO_POINTER(SC_ADDR_LOCAL_TO_INT(connector_addr)); - - sc_element * connector = sc_hash_table_get(cache_table, p_addr); - if (connector == null_ptr) - { - result = sc_storage_get_element_by_addr(connector_addr, &connector); - if (result != SC_RESULT_OK) - break; - - sc_hash_table_insert(cache_table, p_addr, connector); - sc_queue_push(&iter_queue, p_addr); - } - - connector_addr = connector->arc.next_end_in_arc; - } - - sc_monitor_release_read(monitor); - } - - sc_queue_destroy(&iter_queue); - sc_hash_table_destroy(cache_table); - - while (!sc_queue_empty(&addrs_with_not_emitted_erase_events)) - { - sc_addr_hash addr_int = (sc_pointer_to_sc_addr_hash)sc_queue_pop(&addrs_with_not_emitted_erase_events); - addr.seg = SC_ADDR_LOCAL_SEG_FROM_INT(addr_int); - addr.offset = SC_ADDR_LOCAL_OFFSET_FROM_INT(addr_int); - - _sc_storage_element_erase(addr); - } - - sc_queue_destroy(&addrs_with_not_emitted_erase_events); - - result = SC_RESULT_OK; error: - return result; -} - -sc_addr sc_storage_node_new(sc_memory_context const * ctx, sc_type type) -{ - sc_result result; - return sc_storage_node_new_ext(ctx, type, &result); + sc_monitor_release_read(monitor); + return count; } -sc_addr sc_storage_node_new_ext(sc_memory_context const * ctx, sc_type type, sc_result * result) +sc_result sc_storage_get_element_type(sc_memory_context const * ctx, sc_addr addr, sc_type * type) { - sc_addr addr = SC_ADDR_EMPTY; - - if (sc_type_is_not_node(type) && (!sc_type_is(type, sc_type_const) && !sc_type_is(type, sc_type_var))) - { - *result = SC_RESULT_ERROR_ELEMENT_IS_NOT_NODE; - return addr; - } + sc_result result; + sc_element * el = null_ptr; + result = sc_storage_get_element_by_addr(addr, &el); + if (result != SC_RESULT_OK) + goto error; - sc_element * element = sc_storage_allocate_new_element(ctx, &addr); - if (element == null_ptr) - { - *result = SC_RESULT_ERROR_FULL_MEMORY; - return addr; - } + *type = el->flags.type; - element->flags.type = sc_type_node | type; - *result = SC_RESULT_OK; - return addr; +error: + return result; } -sc_addr sc_storage_link_new(sc_memory_context const * ctx, sc_type type) -{ - sc_result result; - return sc_storage_link_new_ext(ctx, type, &result); -} +#define _sc_types_have_not_compatible_mask(type, new_type, mask) \ + ({ \ + sc_type const subtype = type & mask; \ + sc_type const new_subtype = new_type & mask; \ + subtype != sc_type_unknown && subtype != new_subtype; \ + }) -sc_addr sc_storage_link_new_ext(sc_memory_context const * ctx, sc_type type, sc_result * result) +sc_bool sc_storage_is_type_extendable_to(sc_type type, sc_type new_type) { - sc_addr addr = SC_ADDR_EMPTY; - - if (sc_type_is_not_node_link(type)) - { - *result = SC_RESULT_ERROR_ELEMENT_IS_NOT_LINK; - return addr; - } - - sc_element * element = sc_storage_allocate_new_element(ctx, &addr); - if (element == null_ptr) - { - *result = SC_RESULT_ERROR_FULL_MEMORY; - return addr; - } - - element->flags.type = sc_type_node_link | type; - *result = SC_RESULT_OK; - return addr; + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_element_mask)) + return SC_FALSE; + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_constancy_mask)) + return SC_FALSE; + + if (sc_type_is_node_link(type)) + { + if (sc_type_is_not_node_link(new_type)) + return SC_FALSE; + + type = type & ~sc_type_node_link; + new_type = new_type & ~sc_type_node_link; + + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_node_link_mask)) + return SC_FALSE; + } + else if (sc_type_is_node(type)) + { + if (sc_type_is_not_node(new_type)) + return SC_FALSE; + + type = type & ~sc_type_node; + new_type = new_type & ~sc_type_node; + + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_node_mask)) + return SC_FALSE; + } + else if (sc_type_is_connector(type)) + { + if (sc_type_is_not_connector(new_type)) + return SC_FALSE; + + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_connector_mask)) + { + if (sc_type_is_common_edge(type)) + { + if (!sc_type_is_common_edge(new_type)) + return SC_FALSE; + } + else if (sc_type_is_arc(type)) + { + if (!sc_type_is_arc(new_type)) + return SC_FALSE; + + if (sc_type_is_common_arc(type)) + { + if (!sc_type_is_common_arc(new_type)) + return SC_FALSE; + } + else if (sc_type_is_membership_arc(type)) + { + if (!sc_type_is_membership_arc(new_type)) + return SC_FALSE; + } + } + } + + type = type & ~sc_type_connector_mask; + new_type = new_type & ~sc_type_connector_mask; + + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_actuality_mask)) + return SC_FALSE; + + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_permanency_mask)) + return SC_FALSE; + + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_positivity_mask)) + return SC_FALSE; + + if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_fuz_arc)) + return SC_FALSE; + } + + return SC_TRUE; } -void _sc_storage_make_elements_incident_to_arc( - sc_addr connector_addr, - sc_element * arc_el, - sc_addr beg_addr, - sc_element * beg_el, - sc_addr end_addr, - sc_element * end_el, - sc_bool is_reverse, - sc_bool is_loop) +sc_result sc_storage_change_element_subtype(sc_memory_context const * ctx, sc_addr addr, sc_type type) { - sc_element *first_out_arc = null_ptr, *first_in_arc = null_ptr; - - sc_addr first_out_connector_addr = beg_el->first_out_arc; - sc_addr first_in_connector_addr = end_el->first_in_arc; - - sc_monitor * first_out_arc_monitor = null_ptr; - sc_monitor * first_in_arc_monitor = null_ptr; - - if (SC_ADDR_IS_NOT_EQUAL(first_out_connector_addr, beg_addr) - && SC_ADDR_IS_NOT_EQUAL(first_out_connector_addr, end_addr)) - first_out_arc_monitor = - sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, first_out_connector_addr); - if (SC_ADDR_IS_NOT_EQUAL(first_in_connector_addr, beg_addr) - && SC_ADDR_IS_NOT_EQUAL(first_in_connector_addr, end_addr)) - first_in_arc_monitor = - sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, first_in_connector_addr); - - sc_monitor_acquire_write_n(2, first_out_arc_monitor, first_in_arc_monitor); + sc_result result; + sc_element * el = null_ptr; + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_write(monitor); - if (SC_ADDR_IS_NOT_EMPTY(first_out_connector_addr)) - sc_storage_get_element_by_addr(first_out_connector_addr, &first_out_arc); + result = sc_storage_get_element_by_addr(addr, &el); + if (result != SC_RESULT_OK) + goto error; - if (SC_ADDR_IS_NOT_EMPTY(first_in_connector_addr)) - sc_storage_get_element_by_addr(first_in_connector_addr, &first_in_arc); + if (!sc_storage_is_type_extendable_to(el->flags.type, type)) + { + result = SC_RESULT_ERROR_INVALID_PARAMS; + goto error; + } - // set next outgoing sc-arc for our generated arc - if (is_reverse) - { - arc_el->arc.next_end_out_arc = first_out_connector_addr; - arc_el->arc.next_begin_in_arc = first_in_connector_addr; - } - else - { - arc_el->arc.next_begin_out_arc = first_out_connector_addr; - arc_el->arc.next_end_in_arc = first_in_connector_addr; + el->flags.type = type; - if (is_loop) - { - arc_el->arc.next_end_out_arc = first_out_connector_addr; - arc_el->arc.next_begin_in_arc = first_in_connector_addr; - } +error: + sc_monitor_release_write(monitor); + return result; +} - if (first_out_arc) - first_out_arc->arc.prev_begin_out_arc = connector_addr; +sc_result sc_storage_get_arc_begin(sc_memory_context const * ctx, sc_addr addr, sc_addr * result_begin_addr) +{ + *result_begin_addr = SC_ADDR_EMPTY; + sc_result result; + sc_element * el = null_ptr; + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_read(monitor); - if (first_in_arc) - first_in_arc->arc.prev_end_in_arc = connector_addr; - } + result = sc_storage_get_element_by_addr(addr, &el); + if (result != SC_RESULT_OK) + goto error; - sc_monitor_release_write_n(2, first_out_arc_monitor, first_in_arc_monitor); + if (sc_type_is_not_connector(el->flags.type)) + { + result = SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR; + goto error; + } - // set our arc as first output/input at begin/end elements - beg_el->first_out_arc = connector_addr; - end_el->first_in_arc = connector_addr; + *result_begin_addr = el->arc.begin; - ++beg_el->outgoing_arcs_count; - ++end_el->incoming_arcs_count; +error: + sc_monitor_release_read(monitor); + return result; } -#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES -void _sc_storage_update_structure_arcs( - sc_addr connector_addr, - sc_element * arc_el, - sc_addr beg_addr, - sc_addr end_addr, - sc_element * end_el) +sc_result sc_storage_get_arc_end(sc_memory_context const * ctx, sc_addr addr, sc_addr * result_end_addr) { - sc_element * first_in_accessed_arc = null_ptr; - sc_addr first_in_accessed_connector_addr = end_el->first_in_arc_from_structure; - sc_monitor * first_in_accessed_arc_monitor = null_ptr; - - if (SC_ADDR_IS_NOT_EQUAL(first_in_accessed_connector_addr, beg_addr) - && SC_ADDR_IS_NOT_EQUAL(first_in_accessed_connector_addr, end_addr)) - first_in_accessed_arc_monitor = - sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, first_in_accessed_connector_addr); + *result_end_addr = SC_ADDR_EMPTY; + sc_result result; + sc_element * el = null_ptr; + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_read(monitor); - sc_monitor_acquire_write(first_in_accessed_arc_monitor); + result = sc_storage_get_element_by_addr(addr, &el); + if (result != SC_RESULT_OK) + goto error; - if (SC_ADDR_IS_NOT_EMPTY(first_in_accessed_connector_addr)) - sc_storage_get_element_by_addr(first_in_accessed_connector_addr, &first_in_accessed_arc); + if (sc_type_is_not_connector(el->flags.type)) + { + result = SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR; + goto error; + } - arc_el->arc.next_in_arc_from_structure = first_in_accessed_connector_addr; + *result_end_addr = el->arc.end; - if (first_in_accessed_arc) - first_in_accessed_arc->arc.prev_in_arc_from_structure = connector_addr; +error: + sc_monitor_release_read(monitor); + return result; +} - sc_monitor_release_write(first_in_accessed_arc_monitor); +sc_result sc_storage_get_arc_info( + sc_memory_context const * ctx, + sc_addr addr, + sc_addr * result_begin_addr, + sc_addr * result_end_addr) +{ + *result_begin_addr = SC_ADDR_EMPTY; + *result_end_addr = SC_ADDR_EMPTY; + sc_result result; + sc_element * el = null_ptr; + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_read(monitor); + + result = sc_storage_get_element_by_addr(addr, &el); + if (result != SC_RESULT_OK) + goto error; + + if (sc_type_is_not_connector(el->flags.type)) + { + result = SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR; + goto error; + } + + *result_begin_addr = el->arc.begin; + *result_end_addr = el->arc.end; - end_el->first_in_arc_from_structure = connector_addr; +error: + sc_monitor_release_read(monitor); + return result; } -#endif -sc_addr sc_storage_arc_new(sc_memory_context const * ctx, sc_type type, sc_addr beg_addr, sc_addr end_addr) +sc_result sc_storage_set_link_content( + sc_memory_context const * ctx, + sc_addr addr, + sc_stream const * stream, + sc_bool is_searchable_string) { - sc_result result; - return sc_storage_arc_new_ext(ctx, type, beg_addr, end_addr, &result); + sc_result result; + sc_element * el = null_ptr; + sc_char * string = null_ptr; + sc_uint32 string_size = 0; + + if (sc_stream_get_data(stream, &string, &string_size) == SC_FALSE) + { + sc_mem_free(string); + return SC_RESULT_ERROR_STREAM_IO; + } + + if (string == null_ptr) + sc_string_empty(string); + + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_write(monitor); + + result = sc_storage_get_element_by_addr(addr, &el); + if (result != SC_RESULT_OK) + goto error; + + if (sc_type_is_not_node_link(el->flags.type)) + { + result = SC_RESULT_ERROR_ELEMENT_IS_NOT_LINK; + goto error; + } + + // Немедленно записываем содержимое для поиска + if (sc_fs_memory_link_string_ext(SC_ADDR_LOCAL_TO_INT(addr), string, string_size, is_searchable_string) + != SC_FS_MEMORY_OK) + { + result = SC_RESULT_ERROR_FILE_MEMORY_IO; + goto error; + } + + // Добавляем операцию установки содержимого в буфер + if (!_sc_storage_add_operation(SC_OP_SET_LINK_CONTENT, addr, 0, SC_ADDR_EMPTY, SC_ADDR_EMPTY, string, string_size)) + { + result = SC_RESULT_ERROR_FULL_MEMORY; + goto error; + } + + sc_event_emit( + ctx, addr, sc_event_before_change_link_content_addr, SC_ADDR_EMPTY, 0, SC_ADDR_EMPTY, null_ptr, SC_ADDR_EMPTY); + +error: + sc_monitor_release_write(monitor); + sc_mem_free(string); + return result; } -sc_addr sc_storage_arc_new_ext( - sc_memory_context const * ctx, - sc_type type, - sc_addr beg_addr, - sc_addr end_addr, - sc_result * result) +sc_result sc_storage_get_link_content(sc_memory_context const * ctx, sc_addr addr, sc_stream ** stream) { - sc_addr connector_addr = SC_ADDR_EMPTY; + *stream = null_ptr; + sc_result result; + sc_element * el = null_ptr; + sc_char * string = null_ptr; + sc_uint32 string_size = 0; - if (sc_type_is_not_connector(type)) - { - *result = SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR; - return connector_addr; - } + sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); + sc_monitor_acquire_read(monitor); - if (SC_ADDR_IS_EMPTY(beg_addr) || SC_ADDR_IS_EMPTY(end_addr)) - { - *result = SC_RESULT_ERROR_ADDR_IS_NOT_VALID; - return connector_addr; - } + result = sc_storage_get_element_by_addr(addr, &el); + if (result != SC_RESULT_OK) + goto error; - sc_element *beg_el = null_ptr, *end_el = null_ptr; + if (sc_type_is_not_node_link(el->flags.type)) + { + result = SC_RESULT_ERROR_ELEMENT_IS_NOT_LINK; + goto error; + } - sc_element * arc_el = sc_storage_allocate_new_element(ctx, &connector_addr); - if (arc_el == null_ptr) - { - *result = SC_RESULT_ERROR_FULL_MEMORY; - return connector_addr; - } + sc_fs_memory_status const fs_memory_status = + sc_fs_memory_get_string_by_link_hash(SC_ADDR_LOCAL_TO_INT(addr), &string, &string_size); + if (fs_memory_status != SC_FS_MEMORY_OK && fs_memory_status != SC_FS_MEMORY_NO_STRING) + { + result = SC_RESULT_ERROR_FILE_MEMORY_IO; + goto error; + } - arc_el->flags.type = type; - arc_el->arc.begin = beg_addr; - arc_el->arc.end = end_addr; + sc_monitor_release_read(monitor); - sc_bool is_edge = sc_type_has_subtype(type, sc_type_common_edge); - sc_bool is_not_loop = SC_ADDR_IS_NOT_EQUAL(beg_addr, end_addr); + if (string == null_ptr) + sc_string_empty(string); - // try to lock begin and end elements - sc_monitor * beg_monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, beg_addr); - sc_monitor * end_monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, end_addr); - sc_monitor_acquire_write_n(2, beg_monitor, end_monitor); + *stream = sc_stream_memory_new(string, string_size, SC_STREAM_FLAG_READ, SC_TRUE); + return SC_RESULT_OK; - *result = sc_storage_get_element_by_addr(beg_addr, &beg_el); - if (*result != SC_RESULT_OK) - goto error; +error: + sc_monitor_release_read(monitor); + *stream = null_ptr; + return result; +} - *result = sc_storage_get_element_by_addr(end_addr, &end_el); - if (*result != SC_RESULT_OK) - goto error; +sc_result sc_storage_find_links_with_content_string( + sc_memory_context const * ctx, + sc_stream const * stream, + sc_link_handler * link_handler) +{ + sc_result result = SC_RESULT_OK; + sc_char * string = null_ptr; + sc_uint32 string_size = 0; - // lock arcs to change output/input list - _sc_storage_make_elements_incident_to_arc( - connector_addr, arc_el, beg_addr, beg_el, end_addr, end_el, SC_FALSE, !is_not_loop); - if (is_edge && is_not_loop) - _sc_storage_make_elements_incident_to_arc( - connector_addr, arc_el, end_addr, end_el, beg_addr, beg_el, SC_TRUE, SC_FALSE); + if (sc_stream_get_data(stream, &string, &string_size) != SC_TRUE) + { + result = SC_RESULT_ERROR_STREAM_IO; + goto error; + } -#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES - if (sc_type_is_structure_and_arc(beg_el->flags.type, type)) - _sc_storage_update_structure_arcs(connector_addr, arc_el, beg_addr, end_addr, end_el); -#endif + if (string == null_ptr) + { + string_size = 0; + sc_string_empty(string); + } - // emit events - if (is_edge && is_not_loop) - { - sc_event_emit( - ctx, end_addr, sc_event_after_generate_edge_addr, connector_addr, type, beg_addr, null_ptr, SC_ADDR_EMPTY); - sc_event_emit( - ctx, beg_addr, sc_event_after_generate_edge_addr, connector_addr, type, end_addr, null_ptr, SC_ADDR_EMPTY); - } - else - { - sc_event_emit( - ctx, - beg_addr, - sc_event_after_generate_outgoing_arc_addr, - connector_addr, - type, - end_addr, - null_ptr, - SC_ADDR_EMPTY); - sc_event_emit( - ctx, - end_addr, - sc_event_after_generate_incoming_arc_addr, - connector_addr, - type, - beg_addr, - null_ptr, - SC_ADDR_EMPTY); - } - - sc_event_emit( - ctx, end_addr, sc_event_after_generate_connector_addr, connector_addr, type, beg_addr, null_ptr, SC_ADDR_EMPTY); - sc_event_emit( - ctx, beg_addr, sc_event_after_generate_connector_addr, connector_addr, type, end_addr, null_ptr, SC_ADDR_EMPTY); + sc_fs_memory_status const fs_memory_status = + sc_fs_memory_get_link_hashes_by_string(string, string_size, link_handler); + if (fs_memory_status != SC_FS_MEMORY_OK && fs_memory_status != SC_FS_MEMORY_NO_STRING) + result = SC_RESULT_ERROR_FILE_MEMORY_IO; - sc_monitor_release_write_n(2, beg_monitor, end_monitor); + sc_mem_free(string); - *result = SC_RESULT_OK; - return connector_addr; error: - sc_storage_free_element(connector_addr); - sc_monitor_release_write_n(2, beg_monitor, end_monitor); - return SC_ADDR_EMPTY; + return result; } -sc_uint32 sc_storage_get_element_outgoing_arcs_count(sc_memory_context const * ctx, sc_addr addr, sc_result * result) +sc_result sc_storage_find_links_by_content_substring( + sc_memory_context const * ctx, + sc_stream const * stream, + sc_uint32 max_length_to_search_as_prefix, + sc_link_handler * link_handler) { - sc_uint32 count = 0; + sc_result result = SC_RESULT_OK; + sc_char * string = null_ptr; + sc_uint32 string_size = 0; - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_read(monitor); + if (sc_stream_get_data(stream, &string, &string_size) != SC_TRUE) + { + result = SC_RESULT_ERROR_STREAM_IO; + goto error; + } - sc_element * el = null_ptr; - *result = sc_storage_get_element_by_addr(addr, &el); - if (*result != SC_RESULT_OK) - goto error; + if (string == null_ptr) + sc_string_empty(string); + + sc_fs_memory_status const fs_memory_status = + sc_fs_memory_get_link_hashes_by_substring(string, string_size, max_length_to_search_as_prefix, link_handler); + if (fs_memory_status != SC_FS_MEMORY_OK && fs_memory_status != SC_FS_MEMORY_NO_STRING) + result = SC_RESULT_ERROR_FILE_MEMORY_IO; - count = el->outgoing_arcs_count; + sc_mem_free(string); error: - sc_monitor_release_read(monitor); - return count; + return result; } -sc_uint32 sc_storage_get_element_incoming_arcs_count(sc_memory_context const * ctx, sc_addr addr, sc_result * result) +sc_result sc_storage_find_links_contents_by_content_substring( + sc_memory_context const * ctx, + sc_stream const * stream, + sc_uint32 max_length_to_search_as_prefix, + sc_link_handler * link_handler) { - sc_uint32 count = 0; + sc_result result = SC_RESULT_OK; + sc_char * string = null_ptr; + sc_uint32 string_size = 0; - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_read(monitor); + if (sc_stream_get_data(stream, &string, &string_size) != SC_TRUE) + { + result = SC_RESULT_ERROR_STREAM_IO; + goto error; + } - sc_element * el = null_ptr; - *result = sc_storage_get_element_by_addr(addr, &el); - if (*result != SC_RESULT_OK) - goto error; + if (string == null_ptr) + sc_string_empty(string); + + sc_fs_memory_status const fs_memory_status = + sc_fs_memory_get_strings_by_substring(string, string_size, max_length_to_search_as_prefix, link_handler); + if (fs_memory_status != SC_FS_MEMORY_OK && fs_memory_status != SC_FS_MEMORY_NO_STRING) + result = SC_RESULT_ERROR_FILE_MEMORY_IO; - count = el->incoming_arcs_count; + sc_mem_free(string); error: - sc_monitor_release_read(monitor); - return count; + return result; } -sc_result sc_storage_get_element_type(sc_memory_context const * ctx, sc_addr addr, sc_type * type) +sc_result sc_storage_get_elements_stat(sc_stat * stat) { - sc_result result; - - sc_element * el = null_ptr; - - result = sc_storage_get_element_by_addr(addr, &el); - if (result != SC_RESULT_OK) - goto error; + sc_mem_set(stat, 0, sizeof(sc_stat)); + sc_monitor_acquire_read(&storage->segments_monitor); + sc_addr_seg count = storage->segments_count; + sc_monitor_release_read(&storage->segments_monitor); - *type = el->flags.type; + for (sc_addr_seg i = 0; i < count; ++i) + { + sc_segment * segment = _sc_l1_cache_get(storage->l1_cache, i + 1); + if (!segment) + segment = _sc_l2_cache_get(storage->l2_cache, i + 1); + if (!segment) + { + sc_monitor_acquire_read(&storage->segments_monitor); + segment = storage->segments[i]; + sc_monitor_release_read(&storage->segments_monitor); + } + if (segment) + { + sc_monitor_acquire_read(&segment->monitor); + sc_segment_collect_elements_stat(segment, stat); + sc_monitor_release_read(&segment->monitor); + } + } -error: - return result; + return SC_RESULT_OK; } -#define _sc_types_have_not_compatible_mask(type, new_type, mask) \ - ({ \ - sc_type const subtype = type & mask; \ - sc_type const new_subtype = new_type & mask; \ - subtype != sc_type_unknown && subtype != new_subtype; \ - }) -sc_bool sc_storage_is_type_extendable_to(sc_type type, sc_type new_type) +sc_result sc_storage_save(sc_memory_context const * ctx) { - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_element_mask)) - return SC_FALSE; - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_constancy_mask)) - return SC_FALSE; + // Обработать операции в буфере + if (_sc_storage_flush_buffer() != SC_RESULT_OK) + return SC_RESULT_ERROR; - if (sc_type_is_node_link(type)) + // Сохранить всё хранилище + sc_fs_memory_status fs_result = sc_fs_memory_save(storage); + if (fs_result != SC_FS_MEMORY_OK) { - if (sc_type_is_not_node_link(new_type)) - return SC_FALSE; - - type = type & ~sc_type_node_link; - new_type = new_type & ~sc_type_node_link; - - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_node_link_mask)) - return SC_FALSE; + sc_memory_error("Failed to save storage: %d", fs_result); + return SC_RESULT_ERROR; } - else if (sc_type_is_node(type)) - { - if (sc_type_is_not_node(new_type)) - return SC_FALSE; - type = type & ~sc_type_node; - new_type = new_type & ~sc_type_node; - - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_node_mask)) - return SC_FALSE; - } - else if (sc_type_is_connector(type)) + // Очистить флаг is_modified для всех сегментов после успешного сохранения + sc_monitor_acquire_read(&storage->segments_monitor); + for (sc_addr_seg i = 0; i < storage->segments_count; ++i) { - if (sc_type_is_not_connector(new_type)) - return SC_FALSE; - - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_connector_mask)) + sc_segment * segment = _sc_l1_cache_get(storage->l1_cache, i + 1); + if (!segment) + segment = _sc_l2_cache_get(storage->l2_cache, i + 1); + if (!segment) + segment = storage->segments[i]; + if (segment) { - if (sc_type_is_common_edge(type)) - { - if (!sc_type_is_common_edge(new_type)) - return SC_FALSE; - } - else if (sc_type_is_arc(type)) - { - if (!sc_type_is_arc(new_type)) - return SC_FALSE; - - if (sc_type_is_common_arc(type)) - { - if (!sc_type_is_common_arc(new_type)) - return SC_FALSE; - } - else if (sc_type_is_membership_arc(type)) - { - if (!sc_type_is_membership_arc(new_type)) - return SC_FALSE; - } - } + sc_monitor_acquire_write(&segment->monitor); + segment->is_modified = SC_FALSE; + sc_monitor_release_write(&segment->monitor); } - - type = type & ~sc_type_connector_mask; - new_type = new_type & ~sc_type_connector_mask; - - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_actuality_mask)) - return SC_FALSE; - - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_permanency_mask)) - return SC_FALSE; - - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_positivity_mask)) - return SC_FALSE; - - if (_sc_types_have_not_compatible_mask(type, new_type, sc_type_fuz_arc)) - return SC_FALSE; } + sc_monitor_release_read(&storage->segments_monitor); - return SC_TRUE; + return SC_RESULT_OK; } -sc_result sc_storage_change_element_subtype(sc_memory_context const * ctx, sc_addr addr, sc_type type) -{ - sc_result result; - - sc_element * el = null_ptr; - - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_write(monitor); - - result = sc_storage_get_element_by_addr(addr, &el); - if (result != SC_RESULT_OK) - goto error; - if (!sc_storage_is_type_extendable_to(el->flags.type, type)) - { - result = SC_RESULT_ERROR_INVALID_PARAMS; - goto error; - } - el->flags.type = type; -error: - sc_monitor_release_write(monitor); - return result; +void _sc_l1_cache_init(sc_l1_cache * cache, sc_uint32 capacity) +{ + cache->segments = sc_hash_table_init(g_int64_hash, g_int64_equal, null_ptr, null_ptr); + cache->head = null_ptr; + cache->tail = null_ptr; + cache->size = 0; + cache->capacity = capacity; + sc_monitor_init(&cache->monitor); } -sc_result sc_storage_get_arc_begin(sc_memory_context const * ctx, sc_addr addr, sc_addr * result_begin_addr) +void _sc_l1_cache_destroy(sc_l1_cache * cache) { - *result_begin_addr = SC_ADDR_EMPTY; - sc_result result; - - sc_element * el = null_ptr; - - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_read(monitor); - - result = sc_storage_get_element_by_addr(addr, &el); - if (result != SC_RESULT_OK) - goto error; - - if (sc_type_is_not_connector(el->flags.type)) + sc_monitor_acquire_write(&cache->monitor); + sc_hash_table_destroy(cache->segments); + sc_lru_node * node = cache->head; + while (node) { - result = SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR; - goto error; + sc_lru_node * next = node->next; + // Не освобождаем segment, так как он может быть в storage->segments + sc_mem_free(node); + node = next; } - - *result_begin_addr = el->arc.begin; - -error: - sc_monitor_release_read(monitor); - return result; + sc_monitor_destroy(&cache->monitor); } -sc_result sc_storage_get_arc_end(sc_memory_context const * ctx, sc_addr addr, sc_addr * result_end_addr) +void _sc_l1_cache_add(sc_l1_cache * cache, sc_addr_seg seg_num, sc_segment * segment) { - *result_end_addr = SC_ADDR_EMPTY; - sc_result result; - - sc_element * el = null_ptr; - - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_read(monitor); - - result = sc_storage_get_element_by_addr(addr, &el); - if (result != SC_RESULT_OK) - goto error; - - if (sc_type_is_not_connector(el->flags.type)) + sc_monitor_acquire_write(&cache->monitor); + if (cache->size >= cache->capacity) { - result = SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR; - goto error; + sc_lru_node * tail = cache->tail; + if (tail) + { + sc_hash_table_remove(cache->segments, &tail->segment_num); + cache->tail = tail->prev; + if (cache->tail) + cache->tail->next = null_ptr; + else + cache->head = null_ptr; + cache->size--; + _sc_l2_cache_add(storage->l2_cache, tail->segment_num, tail->segment); + sc_mem_free(tail); + } } - *result_end_addr = el->arc.end; - -error: - sc_monitor_release_read(monitor); - return result; + sc_lru_node * node = sc_mem_new(sc_lru_node, 1); + node->segment_num = seg_num; + node->segment = segment; + node->prev = null_ptr; + node->next = cache->head; + if (cache->head) + cache->head->prev = node; + cache->head = node; + if (!cache->tail) + cache->tail = node; + cache->size++; + sc_hash_table_insert(cache->segments, &seg_num, node); + sc_monitor_release_write(&cache->monitor); } -sc_result sc_storage_get_arc_info( - sc_memory_context const * ctx, - sc_addr addr, - sc_addr * result_begin_addr, - sc_addr * result_end_addr) +sc_segment * _sc_l1_cache_get(sc_l1_cache * cache, sc_addr_seg seg_num) { - *result_begin_addr = SC_ADDR_EMPTY; - *result_end_addr = SC_ADDR_EMPTY; - sc_result result; - - sc_element * el = null_ptr; - - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_read(monitor); - - result = sc_storage_get_element_by_addr(addr, &el); - if (result != SC_RESULT_OK) - goto error; - - if (sc_type_is_not_connector(el->flags.type)) + sc_monitor_acquire_write(&cache->monitor); + sc_lru_node * node = sc_hash_table_get(cache->segments, &seg_num); + if (node) { - result = SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR; - goto error; + if (node != cache->head) + { + if (node->prev) + node->prev->next = node->next; + if (node->next) + node->next->prev = node->prev; + if (node == cache->tail) + cache->tail = node->prev; + node->next = cache->head; + node->prev = null_ptr; + cache->head->prev = node; + cache->head = node; + } + sc_segment * segment = node->segment; + sc_monitor_release_write(&cache->monitor); + return segment; } - - *result_begin_addr = el->arc.begin; - *result_end_addr = el->arc.end; - -error: - sc_monitor_release_read(monitor); - return result; + sc_monitor_release_write(&cache->monitor); + return null_ptr; } -sc_result sc_storage_set_link_content( - sc_memory_context const * ctx, - sc_addr addr, - sc_stream const * stream, - sc_bool is_searchable_string) +// Функции для L2-кэша +void _sc_l2_cache_init(sc_l2_cache * cache, sc_uint32 capacity, sc_char * path) { - sc_result result; + cache->segments = sc_hash_table_init(g_int64_hash, g_int64_equal, null_ptr, null_ptr); + cache->cache_path = g_strdup(path); + cache->size = 0; + cache->capacity = capacity; + sc_monitor_init(&cache->monitor); + + int fd = open(cache->cache_path, O_RDWR | O_CREAT | O_DIRECT, 0666); + if (fd >= 0) + close(fd); +} - sc_element * el = null_ptr; +void _sc_l2_cache_destroy(sc_l2_cache * cache) +{ + sc_monitor_acquire_write(&cache->monitor); + sc_hash_table_destroy(cache->segments); + sc_mem_free(cache->cache_path); + sc_monitor_destroy(&cache->monitor); +} - sc_char * string = null_ptr; - sc_uint32 string_size = 0; - if (sc_stream_get_data(stream, &string, &string_size) == SC_FALSE) +sc_result _sc_l2_cache_add(sc_l2_cache * cache, sc_addr_seg seg_num, sc_segment * segment) +{ + sc_monitor_acquire_write(&cache->monitor); + if (cache->size >= cache->capacity) { - sc_mem_free(string); - return SC_RESULT_ERROR_STREAM_IO; + GHashTableIter it; + g_hash_table_iter_init(&it, cache->segments); + gpointer key, value; + if (g_hash_table_iter_next(&it, &key, &value)) + { + sc_addr_seg * old_seg_num = key; + g_hash_table_remove(cache->segments, old_seg_num); + cache->size--; + } } - if (string == null_ptr) - sc_string_empty(string); - - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_write(monitor); - - result = sc_storage_get_element_by_addr(addr, &el); - if (result != SC_RESULT_OK) - goto error; - - if (sc_type_is_not_node_link(el->flags.type)) + int fd = open(cache->cache_path, O_RDWR | O_CREAT | O_DIRECT, 0666); + if (fd < 0) { - result = SC_RESULT_ERROR_ELEMENT_IS_NOT_LINK; - goto error; + sc_memory_error("Failed to open L2 cache file"); + sc_monitor_release_write(&cache->monitor); + return SC_RESULT_ERROR; } - if (sc_fs_memory_link_string_ext(SC_ADDR_LOCAL_TO_INT(addr), string, string_size, is_searchable_string) - != SC_FS_MEMORY_OK) + off_t offset = lseek(fd, 0, SEEK_END); + struct { sc_addr_seg num; off_t offset; } entry = { seg_num, offset + sizeof(entry) }; + if (write(fd, &entry, sizeof(entry)) != sizeof(entry)) { - result = SC_RESULT_ERROR_FILE_MEMORY_IO; - goto error; + sc_memory_error("Failed to write L2 cache entry"); + close(fd); + sc_monitor_release_write(&cache->monitor); + return SC_RESULT_ERROR; } - sc_event_emit( - ctx, addr, sc_event_before_change_link_content_addr, SC_ADDR_EMPTY, 0, SC_ADDR_EMPTY, null_ptr, SC_ADDR_EMPTY); - - sc_monitor_release_write(monitor); - sc_mem_free(string); - - return SC_RESULT_OK; -error: - sc_monitor_release_write(monitor); - sc_mem_free(string); + io_context_t ctx = 0; + struct iocb cb; + struct iocb * cbs[1]; + struct io_event events[1]; - return result; -} - -sc_result sc_storage_get_link_content(sc_memory_context const * ctx, sc_addr addr, sc_stream ** stream) -{ - *stream = null_ptr; - sc_result result; - - sc_element * el = null_ptr; - sc_char * string = null_ptr; - sc_uint32 string_size = 0; - - sc_monitor * monitor = sc_monitor_table_get_monitor_for_addr(&storage->addr_monitors_table, addr); - sc_monitor_acquire_read(monitor); + if (io_setup(1, &ctx) < 0) + { + sc_memory_error("io_setup failed for L2 cache"); + close(fd); + sc_monitor_release_write(&cache->monitor); + return SC_RESULT_ERROR; + } - result = sc_storage_get_element_by_addr(addr, &el); - if (result != SC_RESULT_OK) - goto error; + io_prep_pwrite(&cb, fd, segment, sizeof(sc_segment), entry.offset); + cbs[0] = &cb; - if (sc_type_is_not_node_link(el->flags.type)) + if (io_submit(ctx, 1, cbs) != 1) { - result = SC_RESULT_ERROR_ELEMENT_IS_NOT_LINK; - goto error; + sc_memory_error("io_submit failed for L2 cache"); + io_destroy(ctx); + close(fd); + sc_monitor_release_write(&cache->monitor); + return SC_RESULT_ERROR; } - sc_fs_memory_status const fs_memory_status = - sc_fs_memory_get_string_by_link_hash(SC_ADDR_LOCAL_TO_INT(addr), &string, &string_size); - if (fs_memory_status != SC_FS_MEMORY_OK && fs_memory_status != SC_FS_MEMORY_NO_STRING) + if (io_getevents(ctx, 1, 1, events, NULL) != 1) { - result = SC_RESULT_ERROR_FILE_MEMORY_IO; - goto error; + sc_memory_error("io_getevents failed for L2 cache"); + io_destroy(ctx); + close(fd); + sc_monitor_release_write(&cache->monitor); + return SC_RESULT_ERROR; } - sc_monitor_release_read(monitor); - - if (string == null_ptr) - sc_string_empty(string); - - *stream = sc_stream_memory_new(string, string_size, SC_STREAM_FLAG_READ, SC_TRUE); + io_destroy(ctx); + close(fd); + sc_hash_table_insert(cache->segments, &seg_num, (void *)(intptr_t)entry.offset); + cache->size++; + sc_monitor_release_write(&cache->monitor); return SC_RESULT_OK; -error: - sc_monitor_release_read(monitor); - - *stream = null_ptr; - - return result; } -sc_result sc_storage_find_links_with_content_string( - sc_memory_context const * ctx, - sc_stream const * stream, - sc_link_handler * link_handler) +sc_segment * _sc_l2_cache_get(sc_l2_cache * cache, sc_addr_seg seg_num) { - sc_result result = SC_RESULT_OK; - - sc_char * string = null_ptr; - sc_uint32 string_size = 0; - if (sc_stream_get_data(stream, &string, &string_size) != SC_TRUE) + sc_monitor_acquire_read(&cache->monitor); + void * offset_ptr = sc_hash_table_get(cache->segments, &seg_num); + if (!offset_ptr) { - result = SC_RESULT_ERROR_STREAM_IO; - goto error; + sc_monitor_release_read(&cache->monitor); + return null_ptr; } - if (string == null_ptr) + off_t offset = (off_t)(intptr_t)offset_ptr; + int fd = open(cache->cache_path, O_RDONLY | O_DIRECT); + if (fd < 0) { - string_size = 0; - sc_string_empty(string); + sc_memory_error("Failed to open L2 cache file for reading"); + sc_monitor_release_read(&cache->monitor); + return null_ptr; } - sc_fs_memory_status const fs_memory_status = - sc_fs_memory_get_link_hashes_by_string(string, string_size, link_handler); - if (fs_memory_status != SC_FS_MEMORY_OK && fs_memory_status != SC_FS_MEMORY_NO_STRING) - result = SC_RESULT_ERROR_FILE_MEMORY_IO; - - sc_mem_free(string); - -error: - return result; -} - -sc_result sc_storage_find_links_by_content_substring( - sc_memory_context const * ctx, - sc_stream const * stream, - sc_uint32 max_length_to_search_as_prefix, - sc_link_handler * link_handler) -{ - sc_result result = SC_RESULT_OK; - - sc_char * string = null_ptr; - sc_uint32 string_size = 0; - if (sc_stream_get_data(stream, &string, &string_size) != SC_TRUE) + sc_segment * segment = aligned_alloc(PAGE_SIZE, sizeof(sc_segment)); + if (!segment) { - result = SC_RESULT_ERROR_STREAM_IO; - goto error; + sc_memory_error("Failed to allocate segment for L2 cache"); + close(fd); + sc_monitor_release_read(&cache->monitor); + return null_ptr; } - if (string == null_ptr) - sc_string_empty(string); - - sc_fs_memory_status const fs_memory_status = - sc_fs_memory_get_link_hashes_by_substring(string, string_size, max_length_to_search_as_prefix, link_handler); - if (fs_memory_status != SC_FS_MEMORY_OK && fs_memory_status != SC_FS_MEMORY_NO_STRING) - result = SC_RESULT_ERROR_FILE_MEMORY_IO; - - sc_mem_free(string); - -error: - return result; -} - -sc_result sc_storage_find_links_contents_by_content_substring( - sc_memory_context const * ctx, - sc_stream const * stream, - sc_uint32 max_length_to_search_as_prefix, - sc_link_handler * link_handler) -{ - sc_result result = SC_RESULT_OK; - - sc_char * string = null_ptr; - sc_uint32 string_size = 0; - if (sc_stream_get_data(stream, &string, &string_size) != SC_TRUE) + if (pread(fd, segment, sizeof(sc_segment), offset) != sizeof(sc_segment)) { - result = SC_RESULT_ERROR_STREAM_IO; - goto error; + sc_memory_error("Failed to read segment from L2 cache"); + free(segment); + close(fd); + sc_monitor_release_read(&cache->monitor); + return null_ptr; } + close(fd); - if (string == null_ptr) - sc_string_empty(string); - - sc_fs_memory_status const fs_memory_status = - sc_fs_memory_get_strings_by_substring(string, string_size, max_length_to_search_as_prefix, link_handler); - if (fs_memory_status != SC_FS_MEMORY_OK && fs_memory_status != SC_FS_MEMORY_NO_STRING) - result = SC_RESULT_ERROR_FILE_MEMORY_IO; - sc_mem_free(string); - -error: - return result; + sc_monitor_release_read(&cache->monitor); + return segment; } -sc_result sc_storage_get_elements_stat(sc_stat * stat) -{ - sc_mem_set(stat, 0, sizeof(sc_stat)); - - sc_monitor_acquire_read(&storage->segments_monitor); - sc_addr_seg count = storage->segments_count; - sc_monitor_release_read(&storage->segments_monitor); - - for (sc_addr_seg i = 0; i < count; ++i) - { - sc_segment * segment = storage->segments[i]; - sc_monitor_acquire_read(&segment->monitor); - sc_segment_collect_elements_stat(segment, stat); - sc_monitor_release_read(&segment->monitor); - } - return SC_RESULT_OK; -} -sc_result sc_storage_save(sc_memory_context const * ctx) -{ - return sc_fs_memory_save(storage) == SC_FS_MEMORY_OK ? SC_RESULT_OK : SC_RESULT_ERROR; -} diff --git a/sc-memory/sc-core/src/sc-store/sc_storage.h b/sc-memory/sc-core/src/sc-store/sc_storage.h index 5664e9293..6751397d5 100644 --- a/sc-memory/sc-core/src/sc-store/sc_storage.h +++ b/sc-memory/sc-core/src/sc-store/sc_storage.h @@ -26,8 +26,14 @@ #include "sc-core/sc_link_filter.h" #include "sc-core/sc_stream.h" #include "sc-core/sc-container/sc_list.h" +#include "sc_segment.h" +#include "sc_element.h" + + + +typedef struct _sc_storage sc_storage; + -typedef struct _sc_storage sc_storage; /*! * @brief Initializes the sc-storage with the provided parameters. diff --git a/sc-memory/sc-core/src/sc-store/sc_storage_private.h b/sc-memory/sc-core/src/sc-store/sc_storage_private.h index d0957a05c..735e787bf 100644 --- a/sc-memory/sc-core/src/sc-store/sc_storage_private.h +++ b/sc-memory/sc-core/src/sc-store/sc_storage_private.h @@ -13,8 +13,75 @@ #include "sc-store/sc_storage_dump_manager.h" + #include "sc-store/sc-base/sc_monitor_table_private.h" +#define SC_L1_CACHE_CAPACITY_FACTOR 0.5 // 50% от max_loaded_segments +#define SC_L2_CACHE_CAPACITY_FACTOR 1.0 // 100% от max_loaded_segments +#define SC_BUFFER_MAX_OPERATIONS 1000 // Размер буфера операций + +typedef enum +{ + SC_OP_NODE_NEW, + SC_OP_ARC_NEW, + SC_OP_LINK_NEW, + SC_OP_SET_LINK_CONTENT, + SC_OP_ERASE_ELEMENT +} sc_operation_type; + +typedef struct +{ + sc_operation_type type; + sc_addr addr; // Для узлов, ссылок, дуг + sc_type type_mask; // Для узлов и дуг + sc_addr begin, end; // Для дуг + sc_char * content; // Для содержимого ссылок (копия для логирования) + sc_uint64 content_size; // Размер содержимого +} sc_operation; + + +typedef struct +{ + sc_operation * operations; + sc_uint32 size; + sc_uint32 capacity; + sc_monitor monitor; +} sc_segment_buffer; + + +// Структура узла LRU-кэша +typedef struct _sc_lru_node +{ + sc_addr_seg segment_num; // Номер сегмента + sc_segment * segment; // Указатель на сегмент + struct _sc_lru_node * prev; + struct _sc_lru_node * next; +} sc_lru_node; + +// Структура L1-кэша (DRAM) +typedef struct _sc_l1_cache +{ + sc_hash_table * segments; // Хэш-таблица: sc_addr_seg -> sc_lru_node + sc_lru_node * head; // Самый недавно использованный + sc_lru_node * tail; // Наименее недавно использованный + sc_uint32 size; // Текущий размер + sc_uint32 capacity; // Максимальный размер + sc_monitor monitor; // Монитор для синхронизации +} sc_l1_cache; + +// Структура L2-кэша (SSD/NVMe) +typedef struct _sc_l2_cache +{ + sc_hash_table * segments; // Хэш-таблица: sc_addr_seg -> offset + sc_char * cache_path; // Путь к файлу кэша + sc_uint32 size; // Текущий размер + sc_uint32 capacity; // Максимальный размер + sc_monitor monitor; // Монитор для синхронизации +} sc_l2_cache; + + + + struct _sc_storage { sc_segment ** segments; @@ -29,6 +96,9 @@ struct _sc_storage sc_storage_dump_manager * dump_manager; sc_event_emission_manager * events_emission_manager; sc_event_subscription_manager * events_subscription_manager; + sc_segment_buffer segment_buffer; + sc_l1_cache * l1_cache; // L1-кэш (DRAM) + sc_l2_cache * l2_cache; // L2-кэш (SSD/NVMe) }; struct _sc_storage * sc_storage_get(); @@ -43,4 +113,14 @@ sc_result sc_storage_get_element_by_addr(sc_addr addr, sc_element ** el); sc_result sc_storage_free_element(sc_addr addr); +// Объявления функций кэширования +void _sc_l1_cache_init(sc_l1_cache * cache, sc_uint32 capacity); +void _sc_l1_cache_destroy(sc_l1_cache * cache); +void _sc_l1_cache_add(sc_l1_cache * cache, sc_addr_seg seg_num, sc_segment * segment); +sc_segment * _sc_l1_cache_get(sc_l1_cache * cache, sc_addr_seg seg_num); +void _sc_l2_cache_init(sc_l2_cache * cache, sc_uint32 capacity, sc_char * path); +void _sc_l2_cache_destroy(sc_l2_cache * cache); +sc_result _sc_l2_cache_add(sc_l2_cache * cache, sc_addr_seg seg_num, sc_segment * segment); +sc_segment * _sc_l2_cache_get(sc_l2_cache * cache, sc_addr_seg seg_num); + #endif