file tracking system revision 2
parent
6e9bcb4e36
commit
426b8d5576
15
4ed_system.h
15
4ed_system.h
|
@ -39,18 +39,11 @@ uhash_equal(Unique_Hash a, Unique_Hash b){
|
|||
#define Sys_Set_File_List_Sig(name) void name(File_List *file_list, String directory)
|
||||
typedef Sys_Set_File_List_Sig(System_Set_File_List);
|
||||
|
||||
enum{
|
||||
TrackFileFlag_ExistingOrFail = 0x0,
|
||||
TrackFileFlag_NewOrFail = 0x1,
|
||||
TrackFileFlag_NewAlways = 0x2,
|
||||
TrackFileFlag_ExistingOrNew = 0x3,
|
||||
};
|
||||
#define Sys_Add_Listener_Sig(name) Unique_Hash name(char *filename)
|
||||
typedef Sys_Add_Listener_Sig(System_Track_File);
|
||||
|
||||
#define Sys_Track_File_Sig(name) Unique_Hash name(char *filename, u32 flags)
|
||||
typedef Sys_Track_File_Sig(System_Track_File);
|
||||
|
||||
#define Sys_Untrack_File_Sig(name) i32 name(Unique_Hash index)
|
||||
typedef Sys_Untrack_File_Sig(System_Untrack_File);
|
||||
#define Sys_Remove_Listener_Sig(name) i32 name(char *filename)
|
||||
typedef Sys_Remove_Listener_Sig(System_Untrack_File);
|
||||
|
||||
#define Sys_Get_File_Index_Sig(name) i32 name(char *filename, Unique_Hash *index)
|
||||
typedef Sys_Get_File_Index_Sig(System_Get_File_Index);
|
||||
|
|
|
@ -23,9 +23,6 @@ enum{
|
|||
FileTrack_MemoryTooSmall,
|
||||
FileTrack_OutOfTableMemory,
|
||||
FileTrack_OutOfListenerMemory,
|
||||
FileTrack_FileNotFound,
|
||||
FileTrack_FileAlreadyTracked,
|
||||
FileTrack_FileNotTracked,
|
||||
FileTrack_NoMoreEvents,
|
||||
FileTrack_FileSystemError
|
||||
};
|
||||
|
@ -34,30 +31,7 @@ typedef struct{
|
|||
uint8_t opaque[128];
|
||||
} File_Track_System;
|
||||
|
||||
typedef struct{
|
||||
uint8_t opaque[16];
|
||||
} File_Temp_Handle;
|
||||
|
||||
typedef struct{
|
||||
uint32_t id[4];
|
||||
} File_Index;
|
||||
|
||||
static File_Index
|
||||
zero_file_index(){
|
||||
File_Index a = {0};
|
||||
return(a);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
file_index_eq(File_Index a, File_Index b){
|
||||
return ((a.id[0] == b.id[0]) &&
|
||||
(a.id[1] == b.id[1]) &&
|
||||
(a.id[2] == b.id[2]) &&
|
||||
(a.id[3] == b.id[3]));
|
||||
}
|
||||
|
||||
typedef int32_t File_Track_Result;
|
||||
typedef uint64_t File_Time;
|
||||
|
||||
File_Track_Result
|
||||
init_track_system(File_Track_System *system,
|
||||
|
@ -65,34 +39,10 @@ init_track_system(File_Track_System *system,
|
|||
void *listener_memory, int32_t listener_memory_size);
|
||||
|
||||
File_Track_Result
|
||||
begin_tracking_file(File_Track_System *system, char *name, File_Index *index, File_Time *time);
|
||||
add_listener(File_Track_System *system, char *filename);
|
||||
|
||||
File_Track_Result
|
||||
begin_tracking_new_file(File_Track_System *system, char *name, File_Index *index, File_Time *time);
|
||||
|
||||
File_Track_Result
|
||||
get_file_temp_handle(char *name, File_Temp_Handle *handle);
|
||||
|
||||
File_Track_Result
|
||||
begin_tracking_from_handle(File_Track_System *system, char *name, File_Temp_Handle handle, File_Index *index, File_Time *time);
|
||||
|
||||
File_Track_Result
|
||||
finish_with_temp_handle(File_Temp_Handle handle);
|
||||
|
||||
File_Track_Result
|
||||
get_tracked_file_index(File_Track_System *system, char *name, File_Index *index);
|
||||
|
||||
File_Track_Result
|
||||
stop_tracking_file(File_Track_System *system, File_Index index);
|
||||
|
||||
File_Track_Result
|
||||
count_tracked_files(File_Track_System *system, int32_t *count);
|
||||
|
||||
File_Track_Result
|
||||
get_tracked_file_time(File_Track_System *system, File_Index index, File_Time *time);
|
||||
|
||||
File_Track_Result
|
||||
get_file_time_now(File_Time *time);
|
||||
remove_listener(File_Track_System *system, char *filename);
|
||||
|
||||
File_Track_Result
|
||||
move_track_system(File_Track_System *system, void *mem, int32_t size);
|
||||
|
@ -101,21 +51,7 @@ File_Track_Result
|
|||
expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size);
|
||||
|
||||
File_Track_Result
|
||||
get_change_event(File_Track_System *system, File_Index *index);
|
||||
|
||||
File_Track_Result
|
||||
get_tracked_file_size(File_Track_System *system, File_Index index, uint32_t *size);
|
||||
|
||||
File_Track_Result
|
||||
get_tracked_file_data(File_Track_System *system, File_Index index, void *mem, uint32_t size);
|
||||
|
||||
File_Track_Result
|
||||
rewrite_tracked_file(File_Track_System *system, File_Index index,
|
||||
void *data, int32_t size, File_Time *time);
|
||||
|
||||
File_Track_Result
|
||||
rewrite_arbitrary_file(File_Track_System *system, char *filename,
|
||||
void *data, int32_t size, File_Time *time);
|
||||
get_change_event(File_Track_System *system, char *buffer, int32_t max);
|
||||
|
||||
File_Track_Result
|
||||
shut_down_track_system(File_Track_System *system);
|
||||
|
|
|
@ -29,6 +29,24 @@ Created on: 20.07.2016
|
|||
# define NotImplemented Assert(!"not implemented")
|
||||
#endif
|
||||
|
||||
typedef struct{
|
||||
uint32_t id[4];
|
||||
} File_Index;
|
||||
|
||||
static File_Index
|
||||
zero_file_index(){
|
||||
File_Index a = {0};
|
||||
return(a);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
file_index_eq(File_Index a, File_Index b){
|
||||
return ((a.id[0] == b.id[0]) &&
|
||||
(a.id[1] == b.id[1]) &&
|
||||
(a.id[2] == b.id[2]) &&
|
||||
(a.id[3] == b.id[3]));
|
||||
}
|
||||
|
||||
typedef uint32_t rptr32;
|
||||
|
||||
typedef struct DLL_Node {
|
||||
|
@ -42,6 +60,7 @@ typedef struct {
|
|||
int32_t user_count;
|
||||
|
||||
char dir_name[512];
|
||||
int32_t dir_name_len;
|
||||
|
||||
// TODO(allen): I am only ever using one thread
|
||||
// for reading results. So is it possible to
|
||||
|
@ -58,7 +77,6 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
HANDLE iocp;
|
||||
HANDLE thread;
|
||||
CRITICAL_SECTION table_lock;
|
||||
|
||||
void *tables;
|
||||
|
@ -69,32 +87,17 @@ typedef struct {
|
|||
typedef struct {
|
||||
int32_t size;
|
||||
uint32_t tracked_count;
|
||||
uint32_t tracked_dir_count;
|
||||
uint32_t max;
|
||||
uint32_t change_write_pos;
|
||||
uint32_t change_read_pos;
|
||||
rptr32 change_queue;
|
||||
rptr32 file_table;
|
||||
} File_Track_Tables;
|
||||
|
||||
typedef struct {
|
||||
HANDLE file, dir;
|
||||
HANDLE dir;
|
||||
File_Index hash;
|
||||
union {
|
||||
Directory_Listener_Node *listener_node;
|
||||
struct {
|
||||
int32_t change_pos;
|
||||
int32_t skip_change;
|
||||
};
|
||||
};
|
||||
Directory_Listener_Node *listener_node;
|
||||
} File_Track_Entry;
|
||||
|
||||
typedef struct {
|
||||
File_Index index;
|
||||
int32_t still_active;
|
||||
} File_Change_Record;
|
||||
|
||||
#define FILE_ENTRY_COST (sizeof(File_Change_Record) + sizeof(File_Track_Entry))
|
||||
#define FILE_ENTRY_COST (sizeof(File_Track_Entry))
|
||||
|
||||
#define to_vars_(s) ((File_Track_Vars*)(s))
|
||||
#define to_tables(v) ((File_Track_Tables*)(v->tables))
|
||||
|
@ -220,12 +223,7 @@ get_file_entry(File_Track_Tables *tables, File_Index index){
|
|||
return(entry);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
internal_get_tracked_file_index(File_Track_Tables *tables,
|
||||
char *name,
|
||||
File_Index *index,
|
||||
File_Track_Entry **entry);
|
||||
|
||||
#if 0
|
||||
static DWORD
|
||||
directory_watching(LPVOID ptr){
|
||||
File_Track_Vars *vars = to_vars_(ptr);
|
||||
|
@ -341,6 +339,7 @@ directory_watching(LPVOID ptr){
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
File_Track_Result
|
||||
init_track_system(File_Track_System *system,
|
||||
|
@ -359,14 +358,11 @@ init_track_system(File_Track_System *system,
|
|||
{
|
||||
tables->size = table_memory_size;
|
||||
tables->tracked_count = 0;
|
||||
tables->tracked_dir_count = 0;
|
||||
|
||||
int32_t likely_entry_size = FILE_ENTRY_COST;
|
||||
int32_t max_number_of_entries = (table_memory_size - sizeof(*tables)) / likely_entry_size;
|
||||
|
||||
tables->change_queue = sizeof(*tables);
|
||||
tables->file_table = tables->change_queue +
|
||||
sizeof(File_Change_Record)*max_number_of_entries;
|
||||
tables->file_table = sizeof(*tables);
|
||||
tables->max = max_number_of_entries;
|
||||
}
|
||||
|
||||
|
@ -381,16 +377,10 @@ init_track_system(File_Track_System *system,
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE(allen): Launch the file watching thread
|
||||
// NOTE(allen): Prepare the file tracking synchronization objects.
|
||||
{
|
||||
InitializeCriticalSection(&vars->table_lock);
|
||||
|
||||
vars->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1);
|
||||
|
||||
vars->thread = CreateThread(0, 0,
|
||||
directory_watching,
|
||||
system,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
result = FileTrack_Good;
|
||||
|
@ -432,12 +422,6 @@ internal_get_file_index(BY_HANDLE_FILE_INFORMATION info){
|
|||
return(hash);
|
||||
}
|
||||
|
||||
static void
|
||||
internal_set_time(File_Time *time, BY_HANDLE_FILE_INFORMATION info){
|
||||
*time = (((uint64_t)info.ftLastWriteTime.dwHighDateTime << 32) |
|
||||
(info.ftLastWriteTime.dwLowDateTime));
|
||||
}
|
||||
|
||||
static void
|
||||
internal_free_slot(File_Track_Tables *tables, File_Track_Entry *entry){
|
||||
Assert(!entry_is_available(entry));
|
||||
|
@ -452,328 +436,138 @@ internal_free_slot(File_Track_Tables *tables, File_Track_Entry *entry){
|
|||
}
|
||||
|
||||
File_Track_Result
|
||||
internal_track_file(File_Track_Vars *vars, File_Track_Tables *tables,
|
||||
char *name, HANDLE file, File_Index *index, File_Time *time){
|
||||
|
||||
add_listener(File_Track_System *system, char *filename){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
if (file != INVALID_HANDLE_VALUE){
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
{
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
|
||||
// TODO(allen): make this real!
|
||||
char dir_name[1024];
|
||||
int32_t dir_name_len =
|
||||
internal_get_parent_name(dir_name, sizeof(dir_name), name);
|
||||
internal_get_parent_name(dir_name, sizeof(dir_name), filename);
|
||||
|
||||
HANDLE dir = CreateFile(
|
||||
dir_name,
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
|
||||
0);
|
||||
|
||||
if (dir != INVALID_HANDLE_VALUE){
|
||||
BY_HANDLE_FILE_INFORMATION info = {0};
|
||||
BY_HANDLE_FILE_INFORMATION dir_info = {0};
|
||||
DWORD getinfo_result = GetFileInformationByHandle(dir, &dir_info);
|
||||
|
||||
if (GetFileInformationByHandle(dir, &dir_info)){
|
||||
if (getinfo_result){
|
||||
File_Index dir_hash = internal_get_file_index(dir_info);
|
||||
|
||||
File_Lookup_Result dir_lookup = tracking_system_lookup_entry(tables, dir_hash);
|
||||
|
||||
if (GetFileInformationByHandle(file, &info)){
|
||||
File_Index hash = internal_get_file_index(info);
|
||||
|
||||
if (entry_is_available(dir_lookup.entry)){
|
||||
if (tracking_system_has_space(tables, 2)){
|
||||
Directory_Listener_Node *node = (Directory_Listener_Node*)
|
||||
allocate_node(&vars->free_sentinel);
|
||||
if (node){
|
||||
if (CreateIoCompletionPort(dir, vars->iocp, (ULONG_PTR)node, 1)){
|
||||
ZeroStruct(node->listener.overlapped);
|
||||
if (ReadDirectoryChangesW(dir,
|
||||
node->listener.result,
|
||||
sizeof(node->listener.result),
|
||||
0,
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
0,
|
||||
&node->listener.overlapped,
|
||||
0)){
|
||||
node->listener.dir = dir;
|
||||
node->listener.user_count = 1;
|
||||
|
||||
// TODO(allen): make this real!
|
||||
Assert(dir_name_len < sizeof(node->listener.dir_name));
|
||||
for (int32_t i = 0; i < dir_name_len; ++i){
|
||||
node->listener.dir_name[i] = dir_name[i];
|
||||
}
|
||||
node->listener.dir_name[dir_name_len] = 0;
|
||||
|
||||
dir_lookup.entry->hash = dir_hash;
|
||||
dir_lookup.entry->file = dir;
|
||||
dir_lookup.entry->dir = dir;
|
||||
dir_lookup.entry->listener_node = node;
|
||||
++tables->tracked_count;
|
||||
++tables->tracked_dir_count;
|
||||
|
||||
File_Lookup_Result lookup =
|
||||
tracking_system_lookup_entry(tables, hash);
|
||||
Assert(entry_is_available(lookup.entry));
|
||||
|
||||
lookup.entry->hash = hash;
|
||||
lookup.entry->file = file;
|
||||
lookup.entry->dir = node->listener.dir;
|
||||
lookup.entry->change_pos = -1;
|
||||
++tables->tracked_count;
|
||||
|
||||
*index = hash;
|
||||
internal_set_time(time, info);
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
if (entry_is_available(dir_lookup.entry)){
|
||||
if (tracking_system_has_space(tables, 1)){
|
||||
Directory_Listener_Node *node = (Directory_Listener_Node*)
|
||||
allocate_node(&vars->free_sentinel);
|
||||
if (node){
|
||||
if (CreateIoCompletionPort(dir, vars->iocp, (ULONG_PTR)node, 1)){
|
||||
ZeroStruct(node->listener.overlapped);
|
||||
if (ReadDirectoryChangesW(dir,
|
||||
node->listener.result,
|
||||
sizeof(node->listener.result),
|
||||
0,
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
0,
|
||||
&node->listener.overlapped,
|
||||
0)){
|
||||
node->listener.dir = dir;
|
||||
node->listener.user_count = 1;
|
||||
|
||||
// TODO(allen): make this real!
|
||||
Assert(dir_name_len < sizeof(node->listener.dir_name));
|
||||
for (int32_t i = 0; i < dir_name_len; ++i){
|
||||
node->listener.dir_name[i] = dir_name[i];
|
||||
}
|
||||
node->listener.dir_name[dir_name_len] = 0;
|
||||
node->listener.dir_name_len = dir_name_len;
|
||||
|
||||
dir_lookup.entry->hash = dir_hash;
|
||||
dir_lookup.entry->dir = dir;
|
||||
dir_lookup.entry->listener_node = node;
|
||||
++tables->tracked_count;
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
|
||||
if (result != FileTrack_Good){
|
||||
insert_node(&vars->free_sentinel, &node->node);
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_OutOfListenerMemory;
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
|
||||
if (result != FileTrack_Good){
|
||||
insert_node(&vars->free_sentinel, &node->node);
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_OutOfTableMemory;
|
||||
result = FileTrack_OutOfListenerMemory;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (tracking_system_has_space(tables, 1)){
|
||||
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, hash);
|
||||
if (entry_is_available(lookup.entry)){
|
||||
Directory_Listener_Node *node = dir_lookup.entry->listener_node;
|
||||
++node->listener.user_count;
|
||||
|
||||
lookup.entry->hash = hash;
|
||||
lookup.entry->file = file;
|
||||
lookup.entry->dir = node->listener.dir;
|
||||
lookup.entry->change_pos = -1;
|
||||
++tables->tracked_count;
|
||||
|
||||
*index = hash;
|
||||
internal_set_time(time, info);
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileAlreadyTracked;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_OutOfTableMemory;
|
||||
}
|
||||
result = FileTrack_OutOfTableMemory;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
Directory_Listener_Node *node = dir_lookup.entry->listener_node;
|
||||
++node->listener.user_count;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
|
||||
if (result != FileTrack_Good && dir != 0){
|
||||
CloseHandle(dir);
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
|
||||
if (result != FileTrack_Good && dir != 0 && dir != INVALID_HANDLE_VALUE){
|
||||
CloseHandle(dir);
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotFound;
|
||||
}
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
HANDLE
|
||||
open_file_best_privledges(char *name, DWORD open_disposition){
|
||||
|
||||
// TODO(allen): Try multiple ways to open the
|
||||
// file and get as many privledges as possible.
|
||||
// Expand the API to be capable of reporting
|
||||
// what privledges are available on a file.
|
||||
HANDLE file = CreateFile(
|
||||
name,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
0,
|
||||
open_disposition,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
0);
|
||||
|
||||
return(file);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
begin_tracking_file(File_Track_System *system,
|
||||
char *name,
|
||||
File_Index *index,
|
||||
File_Time *time){
|
||||
remove_listener(File_Track_System *system, char *filename){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
|
||||
{
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
HANDLE file = open_file_best_privledges(name, OPEN_EXISTING);
|
||||
result = internal_track_file(vars, tables, name, file, index, time);
|
||||
if (file != INVALID_HANDLE_VALUE && result != FileTrack_Good){
|
||||
CloseHandle(file);
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
begin_tracking_new_file(File_Track_System *system, char *name, File_Index *index, File_Time *time){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
{
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
HANDLE file = open_file_best_privledges(name, CREATE_ALWAYS);
|
||||
result = internal_track_file(vars, tables, name, file, index, time);
|
||||
if (file != INVALID_HANDLE_VALUE && result != FileTrack_Good){
|
||||
CloseHandle(file);
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
get_file_temp_handle(char *name, File_Temp_Handle *handle){
|
||||
File_Track_Result result = FileTrack_FileNotFound;
|
||||
|
||||
HANDLE h = open_file_best_privledges(name, OPEN_EXISTING);
|
||||
if (h != INVALID_HANDLE_VALUE){
|
||||
*(HANDLE*)handle = h;
|
||||
result = FileTrack_Good;
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
begin_tracking_from_handle(File_Track_System *system, char *name, File_Temp_Handle handle,
|
||||
File_Index *index, File_Time *time){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
{
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
HANDLE file = *(HANDLE*)(&handle);
|
||||
result = internal_track_file(vars, tables, name, file, index, time);
|
||||
}
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
finish_with_temp_handle(File_Temp_Handle handle){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
|
||||
HANDLE file = *(HANDLE*)(&handle);
|
||||
CloseHandle(file);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
internal_get_tracked_file_index(File_Track_Tables *tables,
|
||||
char *name,
|
||||
File_Index *index,
|
||||
File_Track_Entry **entry){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
|
||||
// TODO(allen): Can I open this file with no permissions?
|
||||
HANDLE file = CreateFile(
|
||||
name,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
0);
|
||||
|
||||
if (file != INVALID_HANDLE_VALUE){
|
||||
BY_HANDLE_FILE_INFORMATION info = {0};
|
||||
if (GetFileInformationByHandle(file, &info)){
|
||||
File_Index hash = internal_get_file_index(info);
|
||||
|
||||
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, hash);
|
||||
if (!entry_is_available(lookup.entry)){
|
||||
*index = hash;
|
||||
*entry = lookup.entry;
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotTracked;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
CloseHandle(file);
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotFound;
|
||||
}
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
get_tracked_file_index(File_Track_System *system,
|
||||
char *name,
|
||||
File_Index *index){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
File_Track_Tables *tables = 0;
|
||||
File_Track_Entry *entry = 0;
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
tables = to_tables(vars);
|
||||
result = internal_get_tracked_file_index(tables, name, index, &entry);
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
stop_tracking_file(File_Track_System *system, File_Index index){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
|
||||
File_Track_Entry *entry = get_file_entry(tables, index);
|
||||
|
||||
if (entry){
|
||||
HANDLE dir = entry->dir;
|
||||
if (dir != entry->file){
|
||||
|
||||
// TODO(allen): make this real!
|
||||
char dir_name[1024];
|
||||
internal_get_parent_name(dir_name, sizeof(dir_name), filename);
|
||||
|
||||
HANDLE dir = CreateFile(
|
||||
dir_name,
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
|
||||
0);
|
||||
|
||||
if (dir != INVALID_HANDLE_VALUE){
|
||||
BY_HANDLE_FILE_INFORMATION dir_info = {0};
|
||||
if (GetFileInformationByHandle(dir, &dir_info)){
|
||||
DWORD getinfo_result = GetFileInformationByHandle(dir, &dir_info);
|
||||
|
||||
if (getinfo_result){
|
||||
File_Index dir_hash = internal_get_file_index(dir_info);
|
||||
|
||||
File_Lookup_Result dir_lookup = tracking_system_lookup_entry(tables, dir_hash);
|
||||
|
||||
Assert(!entry_is_available(dir_lookup.entry));
|
||||
|
@ -782,83 +576,26 @@ stop_tracking_file(File_Track_System *system, File_Index index){
|
|||
|
||||
if (node->listener.user_count == 0){
|
||||
insert_node(&vars->free_sentinel, &node->node);
|
||||
CloseHandle(entry->dir);
|
||||
CloseHandle(dir_lookup.entry->dir);
|
||||
internal_free_slot(tables, dir_lookup.entry);
|
||||
--tables->tracked_dir_count;
|
||||
}
|
||||
|
||||
CloseHandle(entry->file);
|
||||
internal_free_slot(tables, entry);
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotTracked;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotTracked;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
count_tracked_files(File_Track_System *system, int32_t *count){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Tables *tables = to_tables(to_vars_(system));
|
||||
*count = tables->tracked_count - tables->tracked_dir_count;
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
get_tracked_file_time(File_Track_System *system, File_Index index, File_Time *time){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
|
||||
File_Track_Entry *entry = get_file_entry(tables, index);
|
||||
|
||||
if (entry){
|
||||
BY_HANDLE_FILE_INFORMATION info = {0};
|
||||
if (GetFileInformationByHandle(entry->file, &info)){
|
||||
internal_set_time(time, info);
|
||||
|
||||
CloseHandle(dir);
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotTracked;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
get_file_time_now(File_Time *time){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
|
||||
FILETIME file_time;
|
||||
SYSTEMTIME system_time;
|
||||
|
||||
GetSystemTime(&system_time);
|
||||
SystemTimeToFileTime(&system_time, &file_time);
|
||||
*time = (((uint64_t)file_time.dwHighDateTime << 32) |
|
||||
(file_time.dwLowDateTime));
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
move_track_system(File_Track_System *system, void *mem, int32_t size){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
|
@ -878,49 +615,13 @@ move_track_system(File_Track_System *system, void *mem, int32_t size){
|
|||
int32_t likely_entry_size = FILE_ENTRY_COST;
|
||||
int32_t max_number_of_entries = (size - sizeof(*tables)) / likely_entry_size;
|
||||
|
||||
tables->change_queue = sizeof(*tables);
|
||||
tables->file_table = tables->change_queue +
|
||||
sizeof(File_Change_Record)*max_number_of_entries;
|
||||
tables->file_table = sizeof(*tables);
|
||||
tables->max = max_number_of_entries;
|
||||
}
|
||||
|
||||
if (tables->max > original_tables->max){
|
||||
uint32_t original_max = original_tables->max;
|
||||
|
||||
uint32_t change_shift = 0;
|
||||
uint32_t change_modulo = original_max;
|
||||
|
||||
// NOTE(allen): Copy the original change queue
|
||||
{
|
||||
uint32_t start_pos = original_tables->change_read_pos;
|
||||
uint32_t end_pos = original_tables->change_write_pos;
|
||||
|
||||
uint32_t changes_count = 0;
|
||||
if (original_tables->change_write_pos < original_tables->change_read_pos){
|
||||
changes_count = original_max + end_pos - start_pos;
|
||||
}
|
||||
else{
|
||||
changes_count = end_pos - start_pos;
|
||||
}
|
||||
|
||||
change_shift = original_max - start_pos;
|
||||
|
||||
File_Change_Record *src = (File_Change_Record*)
|
||||
to_ptr(original_tables, original_tables->change_queue);
|
||||
|
||||
File_Change_Record *dst = (File_Change_Record*)
|
||||
to_ptr(tables, tables->change_queue);
|
||||
|
||||
for (uint32_t i_dst = 0, i_src = start_pos;
|
||||
i_dst < changes_count;
|
||||
++i_dst, i_src = (i_src+1)%original_max){
|
||||
dst[i_dst] = src[i_src];
|
||||
}
|
||||
|
||||
tables->change_read_pos = 0;
|
||||
tables->change_write_pos = changes_count;
|
||||
}
|
||||
|
||||
// NOTE(allen): Rehash the tracking table
|
||||
{
|
||||
File_Track_Entry *entries = (File_Track_Entry*)
|
||||
|
@ -937,17 +638,10 @@ move_track_system(File_Track_System *system, void *mem, int32_t size){
|
|||
Assert(entry_is_available(lookup.entry));
|
||||
|
||||
*lookup.entry = *entry;
|
||||
|
||||
if (lookup.entry->file != lookup.entry->dir &&
|
||||
lookup.entry->change_pos != -1){
|
||||
lookup.entry->change_pos =
|
||||
(lookup.entry->change_pos+change_shift)%change_modulo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tables->tracked_count = original_tables->tracked_count;
|
||||
tables->tracked_dir_count = original_tables->tracked_dir_count;
|
||||
}
|
||||
|
||||
// NOTE(allen): Update to the new table
|
||||
|
@ -990,189 +684,84 @@ expand_track_system_listeners(File_Track_System *system, void *mem, int32_t size
|
|||
}
|
||||
|
||||
File_Track_Result
|
||||
get_change_event(File_Track_System *system, File_Index *index){
|
||||
get_change_event(File_Track_System *system, char *buffer, int32_t max){
|
||||
File_Track_Result result = FileTrack_NoMoreEvents;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
|
||||
uint32_t write_pos = tables->change_write_pos;
|
||||
uint32_t read_pos = tables->change_read_pos;
|
||||
uint32_t max = tables->max;
|
||||
|
||||
File_Change_Record *change_queue =
|
||||
(File_Change_Record*)to_ptr(tables, tables->change_queue);
|
||||
|
||||
while (read_pos != write_pos){
|
||||
File_Change_Record *record = change_queue + read_pos;
|
||||
{
|
||||
OVERLAPPED *overlapped = 0;
|
||||
DWORD length = 0;
|
||||
ULONG_PTR key = 0;
|
||||
|
||||
read_pos = (read_pos + 1) % max;
|
||||
|
||||
if (record->still_active){
|
||||
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, record->index);
|
||||
if (!entry_is_available(lookup.entry)){
|
||||
lookup.entry->change_pos = -1;
|
||||
*index = record->index;
|
||||
result = FileTrack_Good;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tables->change_write_pos = write_pos;
|
||||
tables->change_read_pos = read_pos;
|
||||
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
get_tracked_file_size(File_Track_System *system, File_Index index, uint32_t *size){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
|
||||
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index);
|
||||
if (!entry_is_available(lookup.entry)){
|
||||
DWORD lo, hi;
|
||||
lo = GetFileSize(lookup.entry->file, &hi);
|
||||
Assert(hi == 0);
|
||||
*size = lo;
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotTracked;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
get_tracked_file_data(File_Track_System *system, File_Index index, void *mem, uint32_t size){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
|
||||
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index);
|
||||
if (!entry_is_available(lookup.entry)){
|
||||
DWORD read_size = 0;
|
||||
if (ReadFile(lookup.entry->file, mem, size, &read_size, 0)){
|
||||
if (read_size != size){
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotTracked;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
rewrite_tracked_file(File_Track_System *system, File_Index index,
|
||||
void *data, int32_t size, File_Time *time){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
|
||||
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index);
|
||||
if (!entry_is_available(lookup.entry)){
|
||||
DWORD written = 0;
|
||||
|
||||
lookup.entry->skip_change = 1;
|
||||
SetFilePointer(lookup.entry->file, 0, 0, FILE_BEGIN);
|
||||
if (WriteFile(lookup.entry->file, data, size, &written, 0)){
|
||||
FILETIME file_time;
|
||||
SYSTEMTIME system_time;
|
||||
if (GetQueuedCompletionStatus(vars->iocp,
|
||||
&length,
|
||||
&key,
|
||||
&overlapped,
|
||||
0)){
|
||||
|
||||
GetSystemTime(&system_time);
|
||||
SystemTimeToFileTime(&system_time, &file_time);
|
||||
SetFileTime(lookup.entry->file, 0, 0, &file_time);
|
||||
Directory_Listener *listener_ptr = (Directory_Listener*)overlapped;
|
||||
Directory_Listener listener = *listener_ptr;
|
||||
|
||||
FlushFileBuffers(lookup.entry->file);
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotTracked;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
File_Track_Result
|
||||
rewrite_arbitrary_file(File_Track_System *system, char *filename,
|
||||
void *data, int32_t size, File_Time *time){
|
||||
File_Track_Result result = FileTrack_Good;
|
||||
|
||||
HANDLE file = CreateFile(
|
||||
filename,
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
0);
|
||||
|
||||
if (file != INVALID_HANDLE_VALUE){
|
||||
File_Track_Vars *vars = to_vars_(system);
|
||||
EnterCriticalSection(&vars->table_lock);
|
||||
{
|
||||
File_Track_Tables *tables = to_tables(vars);
|
||||
BY_HANDLE_FILE_INFORMATION info = {0};
|
||||
if (GetFileInformationByHandle(file, &info)){
|
||||
DWORD written = 0;
|
||||
ZeroStruct(listener_ptr->overlapped);
|
||||
ReadDirectoryChangesW(listener_ptr->dir,
|
||||
listener_ptr->result,
|
||||
sizeof(listener_ptr->result),
|
||||
0,
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
0,
|
||||
&listener_ptr->overlapped,
|
||||
0);
|
||||
|
||||
char *listener_buffer = listener.result;
|
||||
DWORD offset = 0;
|
||||
FILE_NOTIFY_INFORMATION *info = 0;
|
||||
|
||||
for (;;){
|
||||
info = (FILE_NOTIFY_INFORMATION*)(listener_buffer + offset);
|
||||
|
||||
File_Index index = internal_get_file_index(info);
|
||||
File_Lookup_Result lookup = tracking_system_lookup_entry(tables, index);
|
||||
if (!entry_is_available(lookup.entry)){
|
||||
lookup.entry->skip_change = 1;
|
||||
int32_t len = info->FileNameLength / 2;
|
||||
if (listener.dir_name_len + 1 + len < max){
|
||||
int32_t pos = 0;
|
||||
char *src = listener.dir_name;
|
||||
for (int32_t i = 0; src[i]; ++i, ++pos){
|
||||
buffer[pos] = src[i];
|
||||
}
|
||||
|
||||
buffer[pos++] = '/';
|
||||
|
||||
for (int32_t i = 0; i < len; ++i, ++pos){
|
||||
buffer[pos] = (char)info->FileName[i];
|
||||
}
|
||||
buffer[pos] = 0;
|
||||
|
||||
result = FileTrack_Good;
|
||||
}
|
||||
else{
|
||||
// TODO(allen): Need some way to stash this result so that if the
|
||||
// user comes back with more memory we can give them the change
|
||||
// notification they missed.
|
||||
result = FileTrack_MemoryTooSmall;
|
||||
}
|
||||
|
||||
WriteFile(file, data, size, &written, 0);
|
||||
|
||||
FILETIME file_time;
|
||||
SYSTEMTIME system_time;
|
||||
|
||||
GetSystemTime(&system_time);
|
||||
SystemTimeToFileTime(&system_time, &file_time);
|
||||
SetFileTime(lookup.entry->file, 0, 0, &file_time);
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileSystemError;
|
||||
if (info->NextEntryOffset != 0){
|
||||
// TODO(allen): We're not ready to handle this yet.
|
||||
// For now I am breaking. In the future, if there
|
||||
// are more results we should stash them and return
|
||||
// them in future calls.
|
||||
offset += info->NextEntryOffset;
|
||||
break;
|
||||
}
|
||||
else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
CloseHandle(file);
|
||||
}
|
||||
else{
|
||||
result = FileTrack_FileNotFound;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
@ -1193,7 +782,7 @@ shut_down_track_system(File_Track_System *system){
|
|||
for (; index < max; ++index){
|
||||
File_Track_Entry *entry = entries + index;
|
||||
if (!entry_is_available(entry)){
|
||||
if (!CloseHandle(entry->file)){
|
||||
if (!CloseHandle(entry->dir)){
|
||||
win32_result = 1;
|
||||
}
|
||||
}
|
||||
|
@ -1202,17 +791,13 @@ shut_down_track_system(File_Track_System *system){
|
|||
if (!CloseHandle(vars->iocp)){
|
||||
win32_result = 1;
|
||||
}
|
||||
TerminateThread(vars->thread, 0);
|
||||
if (!CloseHandle(vars->thread)){
|
||||
win32_result = 1;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&vars->table_lock);
|
||||
|
||||
if (win32_result){
|
||||
result = FileTrack_FileSystemError;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&vars->table_lock);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,436 +0,0 @@
|
|||
/*
|
||||
|
||||
Copy Right FourTech LLC, 2016
|
||||
All Rights Are Reserved
|
||||
|
||||
A test bed for a cross platform file tracking reliability layer.
|
||||
Developed for the use cases in 4coder, but I anticipate that this
|
||||
will be a general problem for me. - Allen Webster
|
||||
|
||||
Created on: 20.07.2016
|
||||
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
#define FILE_TRACK_MAIN
|
||||
|
||||
#include "4tech_file_track.h"
|
||||
#include "4tech_file_track_win32.c"
|
||||
|
||||
#include "filetrack_test.c"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define FILE_TRACK_TEST_DIR1 "w:/filetrack/data/"
|
||||
#define FILE_TRACK_TEST_DIR2 "w:/filetrack/data2/"
|
||||
|
||||
#define FAKE_TRACK_TEST_DIR "w:/filetrack/data1000/"
|
||||
|
||||
#define ALT_NAME_TEST_DIR1 "c:/work/filetrack/data/"
|
||||
#define ALT_NAME_TEST_DIR2 "c:/work/filetrack/data2/"
|
||||
|
||||
static char * test_files[] = {
|
||||
FILE_TRACK_TEST_DIR1"autotab.cpp",
|
||||
FILE_TRACK_TEST_DIR1"basic.cpp",
|
||||
FILE_TRACK_TEST_DIR1"basic.txt",
|
||||
FILE_TRACK_TEST_DIR1"cleanme.cpp",
|
||||
FILE_TRACK_TEST_DIR1"emptyfile.txt",
|
||||
FILE_TRACK_TEST_DIR1"lexer_test.cpp",
|
||||
FILE_TRACK_TEST_DIR1"lexer_test2.cpp",
|
||||
FILE_TRACK_TEST_DIR1"lexer_test3.cpp",
|
||||
FILE_TRACK_TEST_DIR1"saveas.txt",
|
||||
FILE_TRACK_TEST_DIR1"test_large.cpp",
|
||||
|
||||
FILE_TRACK_TEST_DIR2"autotab.cpp",
|
||||
FILE_TRACK_TEST_DIR2"basic.cpp",
|
||||
FILE_TRACK_TEST_DIR2"basic.txt",
|
||||
FILE_TRACK_TEST_DIR2"cleanme.cpp",
|
||||
FILE_TRACK_TEST_DIR2"emptyfile.txt",
|
||||
FILE_TRACK_TEST_DIR2"lexer_test.cpp",
|
||||
FILE_TRACK_TEST_DIR2"lexer_test2.cpp",
|
||||
FILE_TRACK_TEST_DIR2"lexer_test3.cpp",
|
||||
FILE_TRACK_TEST_DIR2"saveas.txt",
|
||||
FILE_TRACK_TEST_DIR2"test_large.cpp",
|
||||
};
|
||||
|
||||
static char * test_alt_files[] = {
|
||||
ALT_NAME_TEST_DIR1"autotab.cpp",
|
||||
ALT_NAME_TEST_DIR1"basic.cpp",
|
||||
ALT_NAME_TEST_DIR1"basic.txt",
|
||||
ALT_NAME_TEST_DIR1"cleanme.cpp",
|
||||
ALT_NAME_TEST_DIR1"emptyfile.txt",
|
||||
ALT_NAME_TEST_DIR1"lexer_test.cpp",
|
||||
ALT_NAME_TEST_DIR1"lexer_test2.cpp",
|
||||
ALT_NAME_TEST_DIR1"lexer_test3.cpp",
|
||||
ALT_NAME_TEST_DIR1"saveas.txt",
|
||||
ALT_NAME_TEST_DIR1"test_large.cpp",
|
||||
|
||||
ALT_NAME_TEST_DIR2"autotab.cpp",
|
||||
ALT_NAME_TEST_DIR2"basic.cpp",
|
||||
ALT_NAME_TEST_DIR2"basic.txt",
|
||||
ALT_NAME_TEST_DIR2"cleanme.cpp",
|
||||
ALT_NAME_TEST_DIR2"emptyfile.txt",
|
||||
ALT_NAME_TEST_DIR2"lexer_test.cpp",
|
||||
ALT_NAME_TEST_DIR2"lexer_test2.cpp",
|
||||
ALT_NAME_TEST_DIR2"lexer_test3.cpp",
|
||||
ALT_NAME_TEST_DIR2"saveas.txt",
|
||||
ALT_NAME_TEST_DIR2"test_large.cpp",
|
||||
};
|
||||
|
||||
static char * fake_files[] = {
|
||||
FAKE_TRACK_TEST_DIR"autotab.cpp",
|
||||
FAKE_TRACK_TEST_DIR"basic.cpp",
|
||||
FAKE_TRACK_TEST_DIR"basic.txt",
|
||||
FAKE_TRACK_TEST_DIR"cleanme.cpp",
|
||||
FAKE_TRACK_TEST_DIR"emptyfile.txt",
|
||||
FAKE_TRACK_TEST_DIR"lexer_test.cpp",
|
||||
FAKE_TRACK_TEST_DIR"lexer_test2.cpp",
|
||||
FAKE_TRACK_TEST_DIR"lexer_test3.cpp",
|
||||
FAKE_TRACK_TEST_DIR"saveas.txt",
|
||||
FAKE_TRACK_TEST_DIR"test_large.cpp",
|
||||
};
|
||||
|
||||
#define ArrayCount(a) ((sizeof(a))/(sizeof(*a)))
|
||||
|
||||
typedef struct{
|
||||
File_Index unique_file_index;
|
||||
File_Time time;
|
||||
} MyFileThing;
|
||||
|
||||
void test_body_A(int32_t size1, int32_t size2){
|
||||
void *mem1 = malloc(size1);
|
||||
void *mem2 = malloc(size2);
|
||||
memset(mem1, 0, size1);
|
||||
|
||||
File_Track_System track = {0};
|
||||
int32_t result = init_track_system(&track,
|
||||
mem1, size1,
|
||||
mem2, size2);
|
||||
assert(result == FileTrack_Good);
|
||||
|
||||
MyFileThing my_file_things[1000];
|
||||
memset(my_file_things, 0, sizeof(my_file_things));
|
||||
|
||||
// NOTE(allen): track in all the test files
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
char *filename = test_files[i];
|
||||
|
||||
File_Index new_file = zero_file_index();
|
||||
File_Time new_time = 0;
|
||||
int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time);
|
||||
while (result != FileTrack_Good){
|
||||
|
||||
switch (result){
|
||||
case FileTrack_OutOfTableMemory:
|
||||
{
|
||||
int32_t new_mem_size = size1*2;
|
||||
void *new_mem = malloc(new_mem_size);
|
||||
|
||||
memset(new_mem, 0, new_mem_size);
|
||||
move_track_system(&track, new_mem, new_mem_size);
|
||||
|
||||
free(mem1);
|
||||
size1 = new_mem_size;
|
||||
mem1 = new_mem;
|
||||
}break;
|
||||
|
||||
case FileTrack_OutOfListenerMemory:
|
||||
{
|
||||
size2 *= 2;
|
||||
void *new_mem = malloc(size2);
|
||||
memset(new_mem, 0, size2);
|
||||
expand_track_system_listeners(&track, new_mem, size2);
|
||||
}break;
|
||||
|
||||
default:
|
||||
{
|
||||
Assert(result == FileTrack_Good);
|
||||
}break;
|
||||
}
|
||||
|
||||
result = begin_tracking_file(&track, filename, &new_file, &new_time);
|
||||
}
|
||||
|
||||
my_file_things[i].unique_file_index = new_file;
|
||||
my_file_things[i].time = new_time;
|
||||
}
|
||||
|
||||
// NOTE(allen): track in fake directories
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(fake_files);
|
||||
++i){
|
||||
File_Index new_file = zero_file_index();
|
||||
File_Time new_time = 0;
|
||||
|
||||
char *filename = fake_files[i];
|
||||
|
||||
int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time);
|
||||
assert(result == FileTrack_FileNotFound);
|
||||
}
|
||||
|
||||
// NOTE(allen): track in already tracked files
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Index new_file = zero_file_index();
|
||||
File_Time new_time = 0;
|
||||
|
||||
char *filename = test_files[i];
|
||||
|
||||
int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time);
|
||||
assert(result == FileTrack_FileAlreadyTracked);
|
||||
}
|
||||
|
||||
// NOTE(allen): track in already tracked files via alt-names
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_alt_files);
|
||||
++i){
|
||||
File_Index new_file = zero_file_index();
|
||||
File_Time new_time = 0;
|
||||
|
||||
char *filename = test_alt_files[i];
|
||||
|
||||
int32_t result = begin_tracking_file(&track, filename, &new_file, &new_time);
|
||||
assert(result == FileTrack_FileAlreadyTracked);
|
||||
}
|
||||
|
||||
// NOTE(allen): each file is still up to date
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Time time = 0;
|
||||
File_Index index = my_file_things[i].unique_file_index;
|
||||
|
||||
get_tracked_file_time(&track, index, &time);
|
||||
|
||||
assert(time == my_file_things[i].time);
|
||||
}
|
||||
|
||||
// NOTE(allen): can still get index from file name
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Index index = my_file_things[i].unique_file_index;
|
||||
|
||||
File_Index result_index1 = zero_file_index();
|
||||
char *filename1 = test_files[i];
|
||||
get_tracked_file_index(&track, filename1, &result_index1);
|
||||
|
||||
File_Index result_index2 = zero_file_index();
|
||||
char *filename2 = test_alt_files[i];
|
||||
get_tracked_file_index(&track, filename2, &result_index2);
|
||||
|
||||
assert(file_index_eq(result_index1, index));
|
||||
assert(file_index_eq(result_index2, index));
|
||||
}
|
||||
|
||||
// NOTE(allen): rewrite all of the files
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
char *filename = test_files[i];
|
||||
|
||||
test_rewrite_file_in_child_proc(filename);
|
||||
}
|
||||
|
||||
// NOTE(allen): each file is behind
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Time time = 0;
|
||||
File_Index index = my_file_things[i].unique_file_index;
|
||||
|
||||
get_tracked_file_time(&track, index, &time);
|
||||
|
||||
assert(my_file_things[i].time < time);
|
||||
}
|
||||
|
||||
// NOTE(allen): poll the tracking system for changed files
|
||||
for (;;){
|
||||
File_Index index = zero_file_index();
|
||||
File_Time time = 0;
|
||||
|
||||
int32_t result = get_change_event(&track, &index);
|
||||
|
||||
if (result == FileTrack_NoMoreEvents){
|
||||
break;
|
||||
}
|
||||
|
||||
result = get_tracked_file_time(&track, index, &time);
|
||||
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Index my_index = my_file_things[i].unique_file_index;
|
||||
if (file_index_eq(my_index, index)){
|
||||
my_file_things[i].time = time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(allen): each file is still up to date (episode 2)
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Time time = 0;
|
||||
File_Index index = my_file_things[i].unique_file_index;
|
||||
|
||||
get_tracked_file_time(&track, index, &time);
|
||||
|
||||
assert(time == my_file_things[i].time);
|
||||
}
|
||||
|
||||
// NOTE(allen): rewrite each file myself
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Index index = my_file_things[i].unique_file_index;
|
||||
File_Time time = test_rewrite_file(&track, index);
|
||||
|
||||
my_file_things[i].time = time;
|
||||
}
|
||||
|
||||
// NOTE(allen): check there are no changed file events
|
||||
{
|
||||
File_Index index = zero_file_index();
|
||||
|
||||
int32_t result = get_change_event(&track, &index);
|
||||
assert(result == FileTrack_NoMoreEvents);
|
||||
}
|
||||
|
||||
// NOTE(allen): rewrite half of the files twice
|
||||
int32_t mid_point = ArrayCount(test_files) / 2;
|
||||
for (int32_t i = 0;
|
||||
i < mid_point;
|
||||
++i){
|
||||
char *filename = test_files[i];
|
||||
|
||||
test_rewrite_file_in_child_proc(filename);
|
||||
}
|
||||
|
||||
for (int32_t i = 0;
|
||||
i < mid_point;
|
||||
++i){
|
||||
char *filename = test_files[i];
|
||||
|
||||
test_rewrite_file_in_child_proc(filename);
|
||||
}
|
||||
|
||||
// NOTE(allen): check number of events equals mid_point
|
||||
int32_t count = 0;
|
||||
for (;;){
|
||||
File_Index index = zero_file_index();
|
||||
File_Time time = 0;
|
||||
|
||||
int32_t result = get_change_event(&track, &index);
|
||||
|
||||
if (result == FileTrack_NoMoreEvents){
|
||||
break;
|
||||
}
|
||||
|
||||
result = get_tracked_file_time(&track, index, &time);
|
||||
|
||||
++count;
|
||||
|
||||
for (int32_t i = 0;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Index my_index = my_file_things[i].unique_file_index;
|
||||
if (file_index_eq(my_index, index)){
|
||||
my_file_things[i].time = time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(count == mid_point);
|
||||
|
||||
// NOTE(allen): untrack half of the files
|
||||
for (int32_t i = 0;
|
||||
i < mid_point;
|
||||
++i){
|
||||
File_Index stop_file = my_file_things[i].unique_file_index;
|
||||
stop_tracking_file(&track, stop_file);
|
||||
}
|
||||
|
||||
// NOTE(allen): untrack the same files again
|
||||
for (int32_t i = 0;
|
||||
i < mid_point;
|
||||
++i){
|
||||
File_Index stop_file = my_file_things[i].unique_file_index;
|
||||
int32_t result = stop_tracking_file(&track, stop_file);
|
||||
assert(result == FileTrack_FileNotTracked);
|
||||
}
|
||||
|
||||
// NOTE(allen): make sure the number of remaining files is correct
|
||||
{
|
||||
int32_t track_count = 0;
|
||||
count_tracked_files(&track, &track_count);
|
||||
assert(track_count == (ArrayCount(test_files) - mid_point));
|
||||
}
|
||||
|
||||
// NOTE(allen): untrack the rest of the files
|
||||
for (int32_t i = mid_point;
|
||||
i < ArrayCount(test_files);
|
||||
++i){
|
||||
File_Index stop_file = my_file_things[i].unique_file_index;
|
||||
stop_tracking_file(&track, stop_file);
|
||||
}
|
||||
|
||||
// NOTE(allen): make sure the system is empty
|
||||
{
|
||||
int32_t track_count = 0;
|
||||
count_tracked_files(&track, &track_count);
|
||||
assert(track_count == 0);
|
||||
}
|
||||
|
||||
// NOTE(allen): finish using the track system
|
||||
{
|
||||
int32_t result = shut_down_track_system(&track);
|
||||
assert(result == FileTrack_Good);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE(allen): test basic tracking logic
|
||||
void test_1(void){
|
||||
int32_t size1 = (16 << 10);
|
||||
int32_t size2 = (16 << 10);
|
||||
test_body_A(size1, size2);
|
||||
}
|
||||
|
||||
// NOTE(allen): test memory expansion system for tables
|
||||
void test_2(void){
|
||||
int32_t size1 = (1 << 10);
|
||||
int32_t size2 = (16 << 10);
|
||||
test_body_A(size1, size2);
|
||||
}
|
||||
|
||||
// NOTE(allen): test memory expansion system for listening nodes
|
||||
void test_3(void){
|
||||
int32_t size1 = (16 << 10);
|
||||
int32_t size2 = (5 << 10);
|
||||
test_body_A(size1, size2);
|
||||
}
|
||||
|
||||
// NOTE(allen): test both memory expansion systems
|
||||
void test_4(void){
|
||||
int32_t size1 = (1 << 10);
|
||||
int32_t size2 = (5 << 10);
|
||||
test_body_A(size1, size2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
test_4();
|
||||
return(0);
|
||||
}
|
||||
|
||||
// BOTTOM
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
/*
|
||||
|
||||
Copy Right FourTech LLC, 2016
|
||||
All Rights Are Reserved
|
||||
|
||||
Helpers for the filetrack_main.c test bed.
|
||||
|
||||
Created on: 20.07.2016
|
||||
|
||||
*/
|
||||
|
||||
// TOP
|
||||
|
||||
#define FILE_REWRITER "w:/filetrack/build/file_rewriter"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
static void
|
||||
rewrite(char *buffer, int32_t size){
|
||||
for (int32_t i = 0;
|
||||
i < size;
|
||||
++i){
|
||||
if (buffer[i] >= 'a' && buffer[i] < 'z'){
|
||||
++buffer[i];
|
||||
}
|
||||
else if (buffer[i] == 'z'){
|
||||
buffer[i] = 'a';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
append(char *buffer, int32_t *pos, char *src){
|
||||
int32_t i = *pos;
|
||||
src -= i;
|
||||
for (; src[i]; ++i){
|
||||
buffer[i] = src[i];
|
||||
}
|
||||
*pos = i;
|
||||
}
|
||||
|
||||
static void
|
||||
test_rewrite_file_in_child_proc(char *filename){
|
||||
char space[2048];
|
||||
int32_t pos = 0;
|
||||
|
||||
append(space, &pos, FILE_REWRITER" ");
|
||||
append(space, &pos, filename);
|
||||
space[pos] = 0;
|
||||
|
||||
int32_t result = system(space);
|
||||
assert(result == 0);
|
||||
}
|
||||
|
||||
#ifndef FILE_TRACK_MAIN
|
||||
|
||||
int
|
||||
main(int argc, char **argv){
|
||||
if (argc == 2){
|
||||
char *filename = argv[1];
|
||||
|
||||
char *mem = 0;
|
||||
int32_t size = 0;
|
||||
|
||||
FILE *file = fopen(filename, "rb");
|
||||
assert(file);
|
||||
fseek(file, 0, SEEK_END);
|
||||
size = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
mem = (char*)malloc(size+1);
|
||||
fread(mem, 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
rewrite(mem, size);
|
||||
|
||||
file = fopen(filename, "wb");
|
||||
assert(file);
|
||||
fwrite(mem, 1, size, file);
|
||||
fclose(file);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static File_Time
|
||||
test_rewrite_file(File_Track_System *system, File_Index index){
|
||||
char *mem = 0;
|
||||
uint32_t size = 0;
|
||||
int32_t result = 0;
|
||||
|
||||
result = get_tracked_file_size(system, index, &size);
|
||||
assert(result == FileTrack_Good);
|
||||
mem = (char*)malloc(size+1);
|
||||
result = get_tracked_file_data(system, index, mem, size);
|
||||
assert(result == FileTrack_Good);
|
||||
|
||||
rewrite(mem, size);
|
||||
|
||||
File_Time time = 0;
|
||||
rewrite_tracked_file(system, index, mem, size, &time);
|
||||
|
||||
free(mem);
|
||||
|
||||
return(time);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// BOTTOM
|
||||
|
Loading…
Reference in New Issue