4coder/code/4ed_working_set.cpp

510 lines
17 KiB
C++
Raw Normal View History

2016-02-21 17:44:23 +00:00
/*
* Mr. 4th Dimention - Allen Webster
*
* 03.01.2017
2016-02-21 17:44:23 +00:00
*
* Working_Set data structure
2016-02-21 17:44:23 +00:00
*
*/
// TOP
internal void
working_set_file_default_settings(Working_Set *working_set, Editing_File *file){
2019-08-12 09:16:04 +00:00
block_zero_struct(&file->settings);
}
2019-08-12 09:16:04 +00:00
////////////////////////////////
2019-08-13 04:19:02 +00:00
internal void
file_change_notification_check(Arena *scratch, Working_Set *working_set, Editing_File *file){
2019-08-13 04:19:02 +00:00
if (file->canon.name_size > 0 && !file->settings.unimportant){
String_Const_u8 name = SCu8(file->canon.name_space, file->canon.name_size);
File_Attributes attributes = system_quick_file_attributes(scratch, name);
if ((attributes.last_write_time > file->attributes.last_write_time) ||
(attributes.last_write_time == 0 && file->attributes.last_write_time > 0)){
if (file->state.save_state == FileSaveState_SavedWaitingForNotification){
file->state.save_state = FileSaveState_Normal;
file->attributes = attributes;
}
else{
file_add_dirty_flag(file, DirtyState_UnloadedChanges);
if (file->external_mod_node.next == 0){
LogEventF(log_string(M), &working_set->arena, file->id, 0, system_thread_get_id(),
"external modification [lwt=0x%llx]", attributes.last_write_time);
dll_insert_back(&working_set->has_external_mod_sentinel, &file->external_mod_node);
system_signal_step(0);
}
2019-08-13 04:19:02 +00:00
}
}
file->attributes = attributes;
}
}
internal void
file_change_notification_thread_main(void *ptr){
Models *models = (Models*)ptr;
Arena arena = make_arena_system();
2019-08-13 04:19:02 +00:00
Working_Set *working_set = &models->working_set;
for (;;){
system_sleep(Thousand(250));
Mutex_Lock lock(working_set->mutex);
2019-08-13 04:19:02 +00:00
if (working_set->active_file_count > 0){
i32 check_count = working_set->active_file_count/16;
check_count = clamp(1, check_count, 100);
Node *used = &working_set->active_file_sentinel;
Node *node = working_set->sync_check_iterator;
if (node == 0 || node == used){
node = used->next;
}
for (i32 i = 0; i < check_count; i += 1){
Editing_File *file = CastFromMember(Editing_File, main_chain_node, node);
node = node->next;
if (node == used){
node = node->next;
}
file_change_notification_check(&arena, working_set, file);
2019-08-13 04:19:02 +00:00
}
working_set->sync_check_iterator = node;
}
}
}
////////////////////////////////
2016-03-08 23:06:27 +00:00
internal Editing_File*
2019-08-12 09:16:04 +00:00
working_set_allocate_file(Working_Set *working_set, Lifetime_Allocator *lifetime_allocator){
Editing_File *file = working_set->free_files;
if (file == 0){
file = push_array(&working_set->arena, Editing_File, 1);
}
2019-08-12 09:16:04 +00:00
else{
sll_stack_pop(working_set->free_files);
2016-03-08 23:06:27 +00:00
}
2019-08-12 09:16:04 +00:00
block_zero_struct(file);
2019-08-12 09:16:04 +00:00
dll_insert_back(&working_set->active_file_sentinel, &file->main_chain_node);
dll_insert_back(&working_set->touch_order_sentinel, &file->touch_node);
2019-08-12 09:16:04 +00:00
working_set->active_file_count += 1;
file->id = working_set->id_counter;
working_set->id_counter += 1;
working_set_file_default_settings(working_set, file);
table_insert(&working_set->id_to_ptr_table,
(u64)file->id, (u64)(PtrAsInt(file)));
return(file);
2016-03-08 23:06:27 +00:00
}
2019-01-31 13:06:42 +00:00
internal void
2019-08-13 04:19:02 +00:00
working_set_free_file(Heap *heap, Working_Set *working_set, Editing_File *file){
if (working_set->sync_check_iterator == &file->main_chain_node){
working_set->sync_check_iterator = working_set->sync_check_iterator->next;
}
2019-01-31 12:38:24 +00:00
dll_remove(&file->main_chain_node);
dll_remove(&file->touch_node);
2019-08-12 09:16:04 +00:00
working_set->active_file_count -= 1;
table_erase(&working_set->id_to_ptr_table, file->id);
sll_stack_push(working_set->free_files, file);
2016-03-08 23:06:27 +00:00
}
2019-01-31 13:06:42 +00:00
internal Editing_File*
2019-08-12 09:16:04 +00:00
working_set_get_file(Working_Set *working_set, Buffer_ID id){
2016-03-08 23:06:27 +00:00
Editing_File *result = 0;
2019-08-12 09:16:04 +00:00
u64 val = 0;
if (table_read(&working_set->id_to_ptr_table, id, &val)){
result = (Editing_File*)(IntAsPtr(val));
2016-03-08 23:06:27 +00:00
}
return(result);
}
2016-03-11 03:33:19 +00:00
internal void
2019-08-13 04:19:02 +00:00
working_set_init(Models *models, Working_Set *working_set){
2019-08-12 09:16:04 +00:00
block_zero_struct(working_set);
working_set->arena = make_arena_system();
2019-06-01 23:58:28 +00:00
2019-08-12 09:16:04 +00:00
working_set->id_counter = 1;
2019-08-12 09:16:04 +00:00
dll_init_sentinel(&working_set->active_file_sentinel);
dll_init_sentinel(&working_set->touch_order_sentinel);
2019-08-12 17:40:02 +00:00
2019-08-12 09:16:04 +00:00
local_const i32 slot_count = 128;
Base_Allocator *allocator = get_base_allocator_system();
2019-08-12 09:16:04 +00:00
working_set->id_to_ptr_table = make_table_u64_u64(allocator, slot_count);
working_set->canon_table = make_table_Data_u64(allocator, slot_count);
working_set->name_table = make_table_Data_u64(allocator, slot_count);
2019-08-13 04:19:02 +00:00
2019-08-13 18:54:15 +00:00
dll_init_sentinel(&working_set->has_external_mod_sentinel);
working_set->mutex = system_mutex_make();
working_set->file_change_thread = system_thread_launch(file_change_notification_thread_main, models);
}
2016-08-24 14:36:29 +00:00
internal Editing_File*
2019-07-13 00:43:17 +00:00
working_set_contains__generic(Working_Set *working_set, Table_Data_u64 *table, String_Const_u8 name){
2016-08-24 14:36:29 +00:00
Editing_File *result = 0;
2019-07-13 00:43:17 +00:00
u64 val = 0;
if (table_read(table, make_data(name.str, name.size), &val)){
2019-08-12 09:16:04 +00:00
result = working_set_get_file(working_set, (Buffer_ID)val);
2016-08-24 14:36:29 +00:00
}
return(result);
}
internal b32
2019-07-13 00:43:17 +00:00
working_set_add__generic(Table_Data_u64 *table, Buffer_ID id, String_Const_u8 name){
return(table_insert(table, make_data(name.str, name.size), id));
2016-08-24 14:36:29 +00:00
}
internal void
2019-07-13 00:43:17 +00:00
working_set_remove__generic(Table_Data_u64 *table, String_Const_u8 name){
table_erase(table, make_data(name.str, name.size));
2016-08-24 14:36:29 +00:00
}
internal Editing_File*
2019-06-01 23:58:28 +00:00
working_set_contains_canon(Working_Set *working_set, String_Const_u8 name){
2019-07-13 00:43:17 +00:00
return(working_set_contains__generic(working_set, &working_set->canon_table, name));
2016-08-24 14:36:29 +00:00
}
internal b32
2019-07-13 00:43:17 +00:00
working_set_canon_add(Working_Set *working_set, Editing_File *file, String_Const_u8 name){
2019-08-12 09:16:04 +00:00
return(working_set_add__generic(&working_set->canon_table, file->id, name));
2016-08-24 14:36:29 +00:00
}
internal void
2019-06-01 23:58:28 +00:00
working_set_canon_remove(Working_Set *working_set, String_Const_u8 name){
2019-07-13 00:43:17 +00:00
working_set_remove__generic(&working_set->canon_table, name);
2016-08-24 14:36:29 +00:00
}
internal Editing_File*
2019-06-01 23:58:28 +00:00
working_set_contains_name(Working_Set *working_set, String_Const_u8 name){
2019-07-13 00:43:17 +00:00
return(working_set_contains__generic(working_set, &working_set->name_table, name));
2016-03-11 03:33:19 +00:00
}
internal b32
2019-09-04 05:31:35 +00:00
working_set_add_name(Working_Set *working_set, Editing_File *file, String_Const_u8 name){
2019-08-12 09:16:04 +00:00
return(working_set_add__generic(&working_set->name_table, file->id, name));
}
internal void
2019-06-01 23:58:28 +00:00
working_set_remove_name(Working_Set *working_set, String_Const_u8 name){
2019-07-13 00:43:17 +00:00
working_set_remove__generic(&working_set->name_table, name);
}
2016-03-08 23:06:27 +00:00
internal Editing_File*
get_file_from_identifier(Working_Set *working_set, Buffer_Identifier buffer){
2016-03-08 23:06:27 +00:00
Editing_File *file = 0;
if (buffer.id != 0){
2019-08-12 09:16:04 +00:00
file = working_set_get_file(working_set, buffer.id);
2016-03-08 23:06:27 +00:00
}
else if (buffer.name != 0){
2019-06-01 23:58:28 +00:00
String_Const_u8 name = SCu8(buffer.name, buffer.name_len);
file = working_set_contains_name(working_set, name);
}
return(file);
2016-03-08 23:06:27 +00:00
}
2016-05-19 16:23:12 +00:00
////////////////////////////////
#if 0
// TODO(allen): Bring the clipboard fully to the custom side.
2020-02-08 00:50:35 +00:00
internal void
working_set_clipboard_clear(Heap *heap, Working_Set *working){
String_Const_u8 *str = working->clipboards;
for (i32 i = 0; i < working->clipboard_size; i += 1, str += 1){
heap_free(heap, str->str);
block_zero_struct(str);
}
working->clipboard_size = 0;
working->clipboard_current = 0;
}
2019-06-01 23:58:28 +00:00
internal String_Const_u8*
working_set_next_clipboard_string(Heap *heap, Working_Set *working, u64 str_size){
i32 clipboard_current = working->clipboard_current;
if (working->clipboard_size == 0){
clipboard_current = 0;
working->clipboard_size = 1;
}
else{
++clipboard_current;
if (clipboard_current >= working->clipboard_max_size){
clipboard_current = 0;
}
else if (working->clipboard_size <= clipboard_current){
working->clipboard_size = clipboard_current + 1;
}
}
2019-06-01 23:58:28 +00:00
String_Const_u8 *result = &working->clipboards[clipboard_current];
working->clipboard_current = clipboard_current;
if (result->str != 0){
2019-06-01 23:58:28 +00:00
heap_free(heap, result->str);
}
2019-06-01 23:58:28 +00:00
u8 *new_str = (u8*)heap_allocate(heap, (i32)(str_size + 1));
*result = SCu8(new_str, str_size);
return(result);
}
2019-06-01 23:58:28 +00:00
internal String_Const_u8*
working_set_clipboard_index(Working_Set *working, i32 index){
2019-06-01 23:58:28 +00:00
String_Const_u8 *result = 0;
i32 size = working->clipboard_size;
i32 current = working->clipboard_current;
if (index >= 0 && size > 0){
index = index % size;
index = current + size - index;
index = index % size;
result = &working->clipboards[index];
}
return(result);
}
#endif
2016-07-20 15:19:54 +00:00
////////////////////////////////
2019-08-04 00:49:40 +00:00
// TODO(allen): get rid of this???
internal b32
get_canon_name(Arena *scratch, String_Const_u8 file_name, Editing_File_Name *canon_name){
2019-08-04 00:49:40 +00:00
Temp_Memory temp = begin_temp(scratch);
String_Const_u8 canonical = system_get_canonical(scratch, file_name);
u64 size = Min(sizeof(canon_name->name_space), canonical.size);
2019-08-04 00:49:40 +00:00
block_copy(canon_name->name_space, canonical.str, size);
canon_name->name_size = size;
end_temp(temp);
2019-06-01 23:58:28 +00:00
file_name_terminate(canon_name);
return(canon_name->name_size > 0);
}
2016-08-24 14:36:29 +00:00
internal void
file_bind_file_name(Working_Set *working_set, Editing_File *file, String_Const_u8 canon_file_name){
2019-06-01 23:58:28 +00:00
Assert(file->unique_name.name_size == 0);
Assert(file->canon.name_size == 0);
u64 size = canon_file_name.size;
2019-06-01 23:58:28 +00:00
size = clamp_top(size, sizeof(file->canon.name_space) - 1);
file->canon.name_size = size;
block_copy(file->canon.name_space, canon_file_name.str, size);
file_name_terminate(&file->canon);
2019-07-13 00:43:17 +00:00
b32 result = working_set_canon_add(working_set, file, string_from_file_name(&file->canon));
2019-06-01 23:58:28 +00:00
Assert(result);
2016-08-24 14:36:29 +00:00
}
internal void
buffer_unbind_file(Working_Set *working_set, Editing_File *file){
2019-06-01 23:58:28 +00:00
Assert(file->unique_name.name_size == 0);
Assert(file->canon.name_size != 0);
working_set_canon_remove(working_set, string_from_file_name(&file->canon));
file->canon.name_size = 0;
2016-08-24 14:36:29 +00:00
}
internal b32
2019-06-01 23:58:28 +00:00
buffer_name_has_conflict(Working_Set *working_set, String_Const_u8 base_name){
b32 hit_conflict = false;
2019-08-12 09:16:04 +00:00
Node *used_nodes = &working_set->active_file_sentinel;
for (Node *node = used_nodes->next;
node != used_nodes;
node = node->next){
2019-01-31 12:38:24 +00:00
Editing_File *file_ptr = CastFromMember(Editing_File, main_chain_node, node);
2019-12-08 02:53:01 +00:00
if (file_ptr && string_match(base_name, string_from_file_name(&file_ptr->unique_name))){
hit_conflict = true;
break;
}
}
return(hit_conflict);
}
2016-08-24 14:36:29 +00:00
internal void
2019-06-01 23:58:28 +00:00
buffer_resolve_name_low_level(Arena *scratch, Working_Set *working_set, Editing_File_Name *name, String_Const_u8 base_name){
u64 size = base_name.size;
2019-06-01 23:58:28 +00:00
size = clamp_top(size, sizeof(name->name_space));
block_copy(name->name_space, base_name.str, size);
String_u8 string = Su8(name->name_space, size, sizeof(name->name_space));
u64 original_size = string.size;
2019-06-01 23:58:28 +00:00
u64 file_x = 0;
for (b32 hit_conflict = true; hit_conflict;){
2019-06-01 23:58:28 +00:00
hit_conflict = buffer_name_has_conflict(working_set, string.string);
if (hit_conflict){
2019-06-01 23:58:28 +00:00
file_x += 1;
string.size = original_size;
Temp_Memory temp = begin_temp(scratch);
String_Const_u8 int_str = string_from_integer(scratch, file_x, 10);
string_append(&string, string_u8_litexpr(" ("));
string_append(&string, int_str);
string_append(&string, string_u8_litexpr(")"));
end_temp(temp);
}
}
2019-06-01 23:58:28 +00:00
name->name_size = string.size;
}
internal void
2019-09-04 05:31:35 +00:00
buffer_bind_name_low_level(Arena *scratch, Working_Set *working_set, Editing_File *file, String_Const_u8 base_name, String_Const_u8 name){
2019-06-01 23:58:28 +00:00
Assert(file->base_name.name_size == 0);
Assert(file->unique_name.name_size == 0);
2016-08-24 14:36:29 +00:00
Editing_File_Name new_name = {};
2019-06-01 23:58:28 +00:00
buffer_resolve_name_low_level(scratch, working_set, &new_name, name);
2016-08-24 14:36:29 +00:00
2019-06-01 23:58:28 +00:00
{
u64 size = base_name.size;
2019-06-01 23:58:28 +00:00
size = clamp_top(size, sizeof(file->base_name.name_space));
block_copy(file->base_name.name_space, base_name.str, size);
file->base_name.name_size = size;
}
2019-06-01 23:58:28 +00:00
{
u64 size = new_name.name_size;
2019-06-01 23:58:28 +00:00
block_copy(file->unique_name.name_space, new_name.name_space, size);
file->unique_name.name_size = size;
}
2019-06-01 23:58:28 +00:00
2019-09-04 05:31:35 +00:00
b32 result = working_set_add_name(working_set, file, string_from_file_name(&file->unique_name));
2019-06-01 23:58:28 +00:00
Assert(result);
2016-08-24 14:36:29 +00:00
}
internal void
buffer_unbind_name_low_level(Working_Set *working_set, Editing_File *file){
2019-06-01 23:58:28 +00:00
Assert(file->base_name.name_size != 0);
Assert(file->unique_name.name_size != 0);
working_set_remove_name(working_set, string_from_file_name(&file->unique_name));
file->base_name.name_size = 0;
file->unique_name.name_size = 0;
2016-08-24 14:36:29 +00:00
}
internal void
buffer_bind_name(Thread_Context *tctx, Models *models, Arena *scratch, Working_Set *working_set, Editing_File *file, String_Const_u8 base_name){
2019-06-01 23:58:28 +00:00
Temp_Memory temp = begin_temp(scratch);
// List of conflict files.
2019-06-01 23:58:28 +00:00
struct Node_Ptr{
Node_Ptr *next;
Editing_File *file_ptr;
};
Node_Ptr *conflict_first = 0;
Node_Ptr *conflict_last = 0;
2019-02-26 23:17:53 +00:00
i32 conflict_count = 0;
{
2019-06-01 23:58:28 +00:00
Node_Ptr *node = push_array(scratch, Node_Ptr, 1);
sll_queue_push(conflict_first, conflict_last, node);
node->file_ptr = file;
conflict_count += 1;
}
2019-08-12 09:16:04 +00:00
Node *used_nodes = &working_set->active_file_sentinel;
2019-06-01 23:58:28 +00:00
for (Node *node = used_nodes->next;
node != used_nodes;
node = node->next){
2019-01-31 12:38:24 +00:00
Editing_File *file_ptr = CastFromMember(Editing_File, main_chain_node, node);
if (file_ptr != 0 && string_match(base_name, string_from_file_name(&file_ptr->unique_name))){
2019-06-01 23:58:28 +00:00
Node_Ptr *new_node = push_array(scratch, Node_Ptr, 1);
sll_queue_push(conflict_first, conflict_last, new_node);
new_node->file_ptr = file_ptr;
conflict_count += 1;
}
}
// Fill conflict array.
Buffer_Name_Conflict_Entry *conflicts = push_array(scratch, Buffer_Name_Conflict_Entry, conflict_count);
2019-06-01 23:58:28 +00:00
{
i32 i = 0;
for (Node_Ptr *node = conflict_first;
node != 0;
node = node->next, i += 1){
Editing_File *file_ptr = node->file_ptr;
Buffer_Name_Conflict_Entry *entry = &conflicts[i];
2019-08-12 09:16:04 +00:00
entry->buffer_id = file_ptr->id;
2019-06-01 23:58:28 +00:00
2019-07-13 00:43:17 +00:00
entry->file_name = push_string_copy(scratch, string_from_file_name(&file_ptr->canon));
entry->base_name = push_string_copy(scratch, base_name);
2019-06-01 23:58:28 +00:00
String_Const_u8 b = base_name;
if (i > 0){
b = string_from_file_name(&file_ptr->unique_name);
}
u64 unique_name_capacity = 256;
2019-06-01 23:58:28 +00:00
u8 *unique_name_buffer = push_array(scratch, u8, unique_name_capacity);
Assert(b.size <= unique_name_capacity);
block_copy(unique_name_buffer, b.str, b.size);
entry->unique_name_in_out = unique_name_buffer;
entry->unique_name_len_in_out = b.size;
entry->unique_name_capacity = unique_name_capacity;
}
}
// Get user's resolution data.
if (models->buffer_name_resolver != 0){
Application_Links app = {};
app.tctx = tctx;
app.cmd_context = models;
models->buffer_name_resolver(&app, conflicts, conflict_count);
}
// Re-bind all of the files
2019-06-01 23:58:28 +00:00
{
i32 i = 0;
for (Node_Ptr *node = conflict_first;
node != 0;
node = node->next, i += 1){
Editing_File *file_ptr = node->file_ptr;
if (file_ptr->unique_name.name_size > 0){
buffer_unbind_name_low_level(working_set, file_ptr);
}
}
}
2019-06-01 23:58:28 +00:00
{
i32 i = 0;
for (Node_Ptr *node = conflict_first;
node != 0;
node = node->next, i += 1){
Editing_File *file_ptr = node->file_ptr;
Buffer_Name_Conflict_Entry *entry = &conflicts[i];
String_Const_u8 unique_name = SCu8(entry->unique_name_in_out, entry->unique_name_len_in_out);
2019-09-04 05:31:35 +00:00
buffer_bind_name_low_level(scratch, working_set, file_ptr, base_name, unique_name);
2019-06-01 23:58:28 +00:00
}
}
2019-06-01 23:58:28 +00:00
end_temp(temp);
}
////////////////////////////////
2019-01-31 12:38:24 +00:00
internal void
file_touch(Working_Set *working_set, Editing_File *file){
Assert(file != 0);
dll_remove(&file->touch_node);
dll_insert(&working_set->touch_order_sentinel, &file->touch_node);
2019-01-31 12:38:24 +00:00
}
internal Editing_File*
file_get_next(Working_Set *working_set, Editing_File *file){
if (file != 0){
Node *node = file->touch_node.next;
file = CastFromMember(Editing_File, touch_node, node);
if (node == &working_set->touch_order_sentinel){
file = 0;
}
}
else{
2019-08-12 09:16:04 +00:00
if (working_set->active_file_count > 0){
Node *node = working_set->touch_order_sentinel.next;
file = CastFromMember(Editing_File, touch_node, node);
}
}
return(file);
}
////////////////////////////////
internal Editing_File*
imp_get_file(Models *models, Buffer_ID buffer_id){
Working_Set *working_set = &models->working_set;
2019-12-08 02:53:01 +00:00
return(working_set_get_file(working_set, buffer_id));
}
2016-02-21 17:44:23 +00:00
// BOTTOM
2019-08-12 09:16:04 +00:00