28 #include "precompiled.h"
41 static const FileId NULL_ID = 0;
42 static const size_t MAX_IDS = 0x10000 -1;
54 FileNode(
const char* atom_fn_)
58 prev_id = next_id = NULL_ID;
63 typedef std::vector<FileNode> FileNodes;
71 static bool is_archivable(
const void* mount)
73 return mount_is_archivable((Mount*)mount);
79 typedef std::map<const char*, FileId> Map;
84 void associate_node_with_fn(
const FileNode& node)
86 FileId
id = id_from_node(&node);
87 const Map::value_type item = std::make_pair(node.atom_fn,
id);
88 std::pair<Map::iterator, bool> ret = map.insert(item);
90 debug_warn(L
"atom_fn already associated with node");
94 FileId id_from_node(
const FileNode* node)
const
97 FileId
id = node - &((*nodes)[0]) +1;
98 ENSURE(id <= nodes->size());
102 FileNode* node_from_id(FileId
id)
const
105 return &(*nodes)[
id-1];
108 FileId id_from_fn(
const char* atom_fn)
const
110 Map::const_iterator cit = map.find(atom_fn);
119 void init(FileNodes* nodes_)
127 for(FileNodes::const_iterator cit =
nodes->begin(); cit !=
nodes->end(); ++cit)
129 const FileNode& node = *cit;
130 associate_node_with_fn(node);
144 static void EntCb(
const char* path,
const CFileInfo* ent, uintptr_t cbData)
146 FileNodes* file_nodes = (FileNodes*)cbData;
152 if(is_archivable(ent->mount))
154 const char* atom_fn = path_Pool()->UniqueCopy(path);
155 file_nodes->push_back(FileNode(atom_fn));
160 FileGatherer(FileNodes& file_nodes)
163 file_nodes.reserve(500);
167 dir_FilteredForEachEntry(
"", VFS_DIR_RECURSIVE, 0, EntCb, (uintptr_t)&file_nodes);
173 if(file_nodes.size() > MAX_IDS)
177 file_nodes.erase(file_nodes.begin() + MAX_IDS, file_nodes.end());
185 typedef u32 ConnectionId;
186 cassert(
sizeof(FileId)*2 <=
sizeof(ConnectionId));
187 static ConnectionId cid_make(FileId first, FileId second)
191 static FileId cid_first(ConnectionId
id)
195 static FileId cid_second(ConnectionId
id)
208 Connection(ConnectionId id_)
209 : id(id_), occurrences(1) {}
212 typedef std::vector<Connection> Connections;
219 class ConnectionBuilder
224 class ConnectionAdder
227 typedef std::map<ConnectionId, Connection*> Map;
228 typedef std::pair<ConnectionId, Connection*> MapItem;
229 typedef Map::const_iterator MapCIt;
235 ConnectionAdder() : prev_id(NULL_ID) {}
237 void operator()(Connections& connections,
const char* new_fn)
239 const bool was_first_call = (prev_id == NULL_ID);
240 FileId
id = id_mgr.id_from_fn(new_fn);
241 const ConnectionId c_id = cid_make(prev_id,
id);
252 MapCIt it = map.find(c_id);
253 const bool already_exists = (it != map.end());
256 Connection* c = it->second;
262 connections.push_back(Connection(c_id));
263 const MapItem item = std::make_pair(c_id, &connections.back());
271 void add_connections_from_runs(
const Trace& t, Connections& connections)
276 ConnectionAdder add_connection;
283 for(
size_t r = 0; r < t.num_runs; r++)
285 const TraceRun& run = t.runs[r];
286 for(
size_t i = 0; i < run.num_ents; i++)
292 if(trace_entry_causes_io(te))
300 if(tree_lookup(te->atom_fn, &tf) ==
INFO::OK)
301 if(is_archivable(tf))
302 add_connection(connections, te->atom_fn);
309 Status run(
const char* trace_filename, Connections& connections)
320 connections.reserve(t.total_ents-1);
322 add_connections_from_runs(t, connections);
342 struct Occurrence_greater:
public std::binary_function<const Connection&, const Connection&, bool>
344 bool operator()(
const Connection& c1,
const Connection& c2)
const
346 return (c1.occurrences > c2.occurrences);
351 void detect_cycleR(FileId
id)
353 FileNode* pnode = id_mgr.node_from_id(
id);
355 FileId next_id = pnode->next_id;
356 if(next_id != NULL_ID)
358 FileNode* pnext = id_mgr.node_from_id(next_id);
362 detect_cycleR(next_id);
365 bool is_cycle_at(FileNodes& file_nodes, FileId node)
368 for(FileNodes::iterator it = file_nodes.begin(); it != file_nodes.end(); ++it)
374 void try_add_edge(FileNodes& file_nodes,
const Connection& c)
376 FileId first_id = cid_first(c.id);
377 FileId second_id = cid_second(c.id);
379 FileNode* first = id_mgr.node_from_id(first_id);
380 FileNode* second = id_mgr.node_from_id(second_id);
382 if(first->next_id != NULL_ID || second->prev_id != NULL_ID)
385 first->next_id = second_id;
386 second->prev_id = first_id;
388 const bool introduced_cycle = is_cycle_at(file_nodes, second_id);
390 ENSURE(introduced_cycle == is_cycle_at(file_nodes, first_id));
395 first->next_id = second->prev_id = NULL_ID;
401 void output_chain(FileNode& node, std::vector<const char*>& fn_vector)
410 FileNode* start = &node;
411 while(start->prev_id != NULL_ID)
412 start = id_mgr.node_from_id(start->prev_id);
415 FileNode* cur = start;
420 fn_vector.push_back(cur->atom_fn);
423 if(cur->next_id == NULL_ID)
425 cur = id_mgr.node_from_id(cur->next_id);
430 TourBuilder(FileNodes& file_nodes, Connections& connections, std::vector<const char*>& fn_vector)
432 std::stable_sort(connections.begin(), connections.end(), Occurrence_greater());
434 for(Connections::iterator it = connections.begin(); it != connections.end(); ++it)
435 try_add_edge(file_nodes, *it);
437 for(FileNodes::iterator it = file_nodes.begin(); it != file_nodes.end(); ++it)
438 output_chain(*it, fn_vector);
451 # define AB_COUNT_LOOSE_FILES 0
453 # define AB_COUNT_LOOSE_FILES 1
461 # define AB_COMPARE_MTIME 0
463 # define AB_COMPARE_MTIME 1
466 #if AB_COUNT_LOOSE_FILES
467 static const ssize_t REBUILD_MAIN_ARCHIVE_THRESHOLD = 50;
468 static const ssize_t BUILD_MINI_ARCHIVE_THRESHOLD = 20;
470 typedef std::set<const char*> FnSet;
471 static FnSet loose_files;
472 static FnSet archived_files;
477 #if AB_COUNT_LOOSE_FILES
480 loose_files.insert(atom_fn);
486 #if AB_COUNT_LOOSE_FILES
487 archived_files.insert(atom_fn);
492 static bool should_rebuild_main_archive(
const char* trace_filename,
493 FilesystemEntries& existing_archives)
497 if(!file_exists(trace_filename))
500 #if AB_COUNT_LOOSE_FILES
503 if(loose_files_only >= REBUILD_MAIN_ARCHIVE_THRESHOLD)
509 time_t most_recent_archive_mtime = 0;
512 for(FilesystemEntries::const_iterator it = existing_archives.begin(); it != existing_archives.end(); ++it)
513 most_recent_archive_mtime = std::max(it->mtime, most_recent_archive_mtime);
516 if(existing_archives.empty() || existing_archives.size() >= 4)
520 const double max_diff = 14*86400;
521 if(difftime(tree_most_recent_mtime(), most_recent_archive_mtime) > max_diff)
533 static ArchiveBuildState ab;
534 static std::vector<const char*> fn_vector;
535 static FilesystemEntries existing_archives;
539 const char* archive_ext;
542 IsArchive(
const char* archive_fn)
544 archive_ext = path_extension(archive_fn);
554 const char* ext = path_extension(ent.
name);
555 if(strcasecmp(archive_ext, ext) != 0)
563 static Status vfs_opt_init(
const char* trace_filename,
const char* archive_fn_fmt,
bool force_build)
565 Filesystem_Posix fsPosix;
568 static NextNumberedFilenameState archive_nfi;
569 dir_NextNumberedFilename(&fsPosix, archive_fn_fmt, &archive_nfi, archive_fn);
577 path_dir_only(archive_fn_fmt, dir);
579 DirEntIt new_end = std::remove_if(existing_archives.begin(), existing_archives.end(), IsArchive(archive_fn));
580 existing_archives.erase(new_end, existing_archives.end());
584 if(!force_build && !should_rebuild_main_archive(trace_filename, existing_archives))
588 FileNodes file_nodes;
589 FileGatherer gatherer(file_nodes);
590 if(file_nodes.empty())
594 id_mgr.init(&file_nodes);
598 Connections connections;
599 ConnectionBuilder cbuilder;
604 TourBuilder builder(file_nodes, connections, fn_vector);
605 fn_vector.push_back(0);
606 Filenames V_fns = &fn_vector[0];
613 static int vfs_opt_continue()
615 int ret = archive_build_continue(&ab);
621 mount_release_all_archives();
627 path_dir_only(archive_fn, archive_dir);
628 (void)path_package_set_dir(&pp, archive_dir);
630 for(DirEntCIt it = existing_archives.begin(); it != existing_archives.end(); ++it)
632 (void)path_package_append_file(&pp, it->name);
633 (void)file_delete(pp.path);
639 (void)mount_rebuild();
652 static bool should_build_mini_archive(
const char*
UNUSED(mini_archive_fn_fmt))
654 #if AB_COUNT_LOOSE_FILES
657 if(loose_files_only >= BUILD_MINI_ARCHIVE_THRESHOLD)
663 static Status build_mini_archive(
const char* mini_archive_fn_fmt)
665 if(!should_build_mini_archive(mini_archive_fn_fmt))
668 #if AB_COUNT_LOOSE_FILES
669 Filenames V_fns =
new const char*[loose_files.size()+1];
670 std::copy(loose_files.begin(), loose_files.end(), &V_fns[0]);
671 V_fns[loose_files.size()] = 0;
675 static NextNumberedFilenameState nfi;
676 Filesystem_Posix fsPosix;
677 dir_NextNumberedFilename(&fsPosix, mini_archive_fn_fmt, &nfi, mini_archive_fn);
696 typedef const char** Filenames;
703 struct ArchiveBuildState
715 Status archive_build_init(
const char* P_archive_filename, Filenames V_fns, ArchiveBuildState* ab)
717 ab->archiveBuilder = CreateArchiveBuilder_Zip(P_archive_filename);
719 ab->codec = ab->archiveBuilder->CreateCompressor();
724 for(ab->num_files = 0; ab->V_fns[ab->num_files]; ab->num_files++) {}
731 int archive_build_continue(ArchiveBuildState* ab)
733 const double end_time =
timer_Time() + 200e-3;
737 const char* V_fn = ab->V_fns[ab->i];
742 if(read_and_compress_file(V_fn, ab->codec, ent, file_contents, buf) ==
INFO::OK)
744 (void)ab->archiveBuilder->AddFile(&ent, file_contents);
745 (void)file_cache_free(buf);
752 int progress_percent = (ab->i*100 / ab->num_files);
754 if(progress_percent == 0)
755 progress_percent = 1;
756 ENSURE(0 < progress_percent && progress_percent <= 100);
757 return progress_percent;
771 void archive_build_cancel(ArchiveBuildState* ab)
782 Status archive_build(
const char* P_archive_filename, Filenames V_fns)
784 ArchiveBuildState ab;
788 int ret = archive_build_continue(&ab);
817 state = DECIDE_IF_BUILD;
821 archive_build_cancel(&ab);
826 const char* archive_fn_fmt,
const char* mini_archive_fn_fmt,
bool force_build)
831 if(
state == DECIDE_IF_BUILD)
833 if(vfs_opt_init(trace_filename, archive_fn_fmt, force_build) !=
INFO::SKIPPED)
845 if(
state == IN_PROGRESS)
847 int ret = vfs_opt_continue();
885 TraceRun(
const TraceEntry* entries,
size_t numEntries)
886 : m_entries(entries), m_numEntries(numEntries)
895 size_t NumEntries()
const
909 std::vector<TraceRun> runs;
914 extern void trace_get(
Trace* t);
915 extern Status trace_write_to_file(
const char* trace_filename);
916 extern Status trace_read_from_file(
const char* trace_filename,
Trace* t);
921 extern bool trace_entry_causes_io(
const TraceEntry* ent);
925 extern Status trace_run(
const char* trace_filename);
936 #error "reimplement me"
946 "------------------------------------------------------------",
952 static std::vector<TraceRun> runs;
957 static std::vector<size_t> run_start_indices;
967 Consequence operator()(
size_t i,
double timestamp,
const char* P_path)
970 if(!strcmp(P_path, delimiter_entry.vfsPathname))
972 run_start_indices.push_back(i+1);
977 const double last_timestamp = cur_timestamp;
978 cur_timestamp = timestamp;
983 (timestamp < last_timestamp))
984 run_start_indices.push_back(i);
989 double cur_timestamp;
997 void trace_get(
Trace* t)
1000 trace_get_raw_ents(ents, num_ents);
1003 if(run_start_indices.empty())
1004 run_start_indices.push_back(0);
1008 t->total_ents = num_ents;
1010 size_t last_start_idx = num_ents;
1012 std::vector<size_t>::reverse_iterator it;
1013 for(it = run_start_indices.rbegin(); it != run_start_indices.rend(); ++it)
1015 const size_t start_idx = *it;
1020 if(last_start_idx == start_idx)
1023 ENSURE(start_idx < t->total_ents);
1025 TraceRun& run = runs[t->num_runs++];
1026 run.num_ents = last_start_idx - start_idx;
1027 run.ents = &ents[start_idx];
1028 last_start_idx = start_idx;
1030 if(t->num_runs == MAX_RUNS)
1034 ENSURE(t->num_runs != 0);
1045 const size_t size = ent->size;
1046 const char* vfsPathname = ent->vfsPathname;
1054 if(!simulatedCache.
Retrieve(vfsPathname, size))
1058 simulatedCache.MarkComplete();
1064 simulatedCache.Release(vfsPathname);
1095 IoBuf buf;
size_t size;
1096 (void)vfs_load(ent->vfsPathname, buf, size, ent->flags);
1100 fileCache.Release(ent->vfsPathname);
1111 Status trace_run(
const char* osPathname)
1115 for(
size_t i = 0; i < trace.
NumEntries(); i++)
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
#define WARN_IF_ERR(expression)
u16 u32_lo(u32 x)
return upper 16-bits
virtual const TraceEntry * Entries() const
virtual Status Load(const OsPath &pathname)
load entries from file.
static Node nodes[os_cpu_MaxProcessors]
bool Retrieve(const VfsPath &pathname, shared_ptr< u8 > &data, size_t &size)
Attempt to retrieve a file's contents from the file cache.
const Status ALL_COMPLETE
void vfs_opt_auto_build_cancel()
const Status NOT_SUPPORTED
u32 u32_from_u16(u16 hi, u16 lo)
assemble u64 from u32
virtual size_t NumEntries() const
u16 u32_hi(u32 x)
return lower 32-bits
cache of file contents with support for zero-copy IO.
#define ENSURE(expr)
ensure the expression <expr> evaluates to non-zero.
int vfs_opt_auto_build(const char *trace_filename, const char *archive_fn_fmt, const char *mini_archive_fn_fmt, bool force_build=false)
void vfs_opt_notify_archived_file(const char *atom_fn)
#define UNREACHABLE
"unreachable code" helpers
#define SAFE_DELETE(p)
delete memory ensuing from new and set the pointer to zero (thus making double-frees safe / a no-op) ...
static const u8 * visited[maxVisited]
i64 Status
Error handling system.
void vfs_opt_notify_loose_file(const char *atom_fn)
Status vfs_opt_rebuild_main_archive(const char *trace_filename, const char *archive_fn_fmt)
shared_ptr< u8 > Reserve(size_t size)
Reserve a chunk of the cache's memory region.
#define WARN_RETURN(status)
#define debug_warn(expr)
display the error dialog with the given text.
#define cassert(expr)
Compile-time assertion.
#define stats_ab_connection(already_exists)
#define RETURN_STATUS_IF_ERR(expression)