code dump. unfortunate lack of commits. rick click menu on files/folders. comparative...
[ede] / libede / src / FileSystemCache.ec
diff --git a/libede/src/FileSystemCache.ec b/libede/src/FileSystemCache.ec
new file mode 100644 (file)
index 0000000..03920bd
--- /dev/null
@@ -0,0 +1,475 @@
+public import "ecere"
+
+public import "FileSystemIterator"
+
+public class FileSystemCacheIterator : InterfaceFileSystemIterator
+{
+public:
+   FileSystemCache cache;
+
+   bool Iterate(char * startPath, bool followLinks)
+   {
+      bool result = true;
+      char * path;
+      FSCacheObject o;
+      if(cache && cache.objects.count)
+      {
+         o = cache.FetchObject(startPath);
+         if(o)
+         {
+            bool listDirEntries;
+            FileSystemCacheIteratorStackFrame frame = null;
+            Array<FileSystemCacheIteratorStackFrame> stack { };
+            if(iterateStartPath)
+            {
+               path = o.GetPath();
+               listDirEntries = OnObject(owner, o.name, path, o.stats, true);
+               if(o.firstChild)
+               {
+                  if(listDirEntries)
+                  {
+                     OnEnteringDirectory(owner, path);
+                     stack.Add((frame = { path, o.firstChild }));
+                     path = null;
+                  }
+                  else
+                     result = false;
+               }
+               delete path;
+            }
+            // else { /* TODO: frame is not initialized!! */ }
+
+            while(frame)
+            {
+               o = frame.o;
+               if(o)
+               {
+                  frame.o = o.nextSibling;
+                  path = o.GetPath();
+                  listDirEntries = OnObject(owner, o.name, path, o.stats, !iterateStartPath && stack.count == 1);
+                  if(o.firstChild)
+                  {
+                     if(listDirEntries)
+                     {
+                        OnEnteringDirectory(owner, path);
+                        stack.Add((frame = { path, o.firstChild }));
+                        path = null;
+                     }
+                     else
+                        result = false;
+                  }
+                  delete path;
+               }
+               else
+               {
+                  if(stack.count > (iterateStartPath ? 0 : 1))
+                     OnLeavingDirectory(owner, frame.path);
+                  delete frame;
+                  stack.lastIterator.Remove();
+                  frame = stack.count == 0 ? null : stack.lastIterator.data;
+               }
+            }
+            if(stack.count != 0)
+               PrintLn("dddddddd");
+            delete stack;
+         }
+      }
+      return result;
+   }
+}
+
+class FileSystemCacheIteratorStackFrame : struct
+{
+public:
+   String path;
+   FSCacheObject o;
+
+private:
+   ~FileSystemCacheIteratorStackFrame()
+   {
+      delete path;
+   }
+};
+
+class FileSystemCacheBits
+{
+   bool indexInodes:1;
+}
+
+public class DummyFileSystemCacheWindow : Window
+{
+public:
+   FileSystemCache cache { };
+}
+
+public class FileSystemCache
+{
+public:
+   Map<FSCacheObject, FSCacheRoot> roots { };
+   Array<FSCacheObject> objects { };
+   Array<FSCacheObject> normalParentStack;
+   Map<uint, FileSystemDeviceCache> deviceCaches { };
+
+   property bool indexInodes { set { bits.indexInodes = value; } get { return bits.indexInodes; } };
+
+   void/*FileSystemCache*/ ::Cache(char * path, bool indexInodes, DummyFileSystemCacheWindow dummyWindow)
+   {
+      FileSystemCache cache = dummyWindow.cache;
+      // TOIMP: for now we will assume the cache includes files from a single domain (partition/share)
+      // TOIMP: would like to use full blown file iterator driver system to support other types of file system, archives, etc
+      FileAttribs pathAttribs = FileExists(path);
+      if(pathAttribs)
+      {
+         cache = dummyWindow.cache;// = { };
+         cache.indexInodes = indexInodes;
+         cache.normalParentStack = { };
+         //cache.domainParentStack = { };
+         if(pathAttribs.isDirectory)
+         {
+            FileSystemIterator fsi
+            {
+               owner = dummyWindow; //owner = (void *)this;
+               iterateStartPath = true;
+
+               // COMPILER TOFIX: this will not compile (error: method class must be derived from class) -- bool Whatever/*FileSystemCache*/::OnFolder(...)
+               bool /*FileSystemCache*/DummyFileSystemCacheWindow::OnObject(char * name, char * path, FileStats stats, bool isRootObject)
+               {
+                  bool result = true;
+                  FileSystemCache cache = this.cache;
+                  uint dev = stats.dev;
+                  uint inode = stats.inode;
+                  char * p;
+                  FSCacheObject parent = isRootObject ? null : cache.normalParentStack.lastIterator.data;
+                  FSCacheObject o { parent, name = CopyString(isRootObject ? path : name), stats = stats };
+                  FileStats s;
+                  if(isRootObject)
+                  {
+                     // TODO see if we can find a parent for it
+                     // see if we already have a FSCacheObject for it, if so, update
+                     // distant parents -- when there are gaps.. maybe the parent's parent is present but not the parent
+                     // keep a list of those and see if they can be pluged in after a Cache run
+                     cache.roots[o] = FSCacheRoot { cache.objects.count, GetPathDirNamesArray(path) };
+                  }
+                  else
+                  {
+                     if(!parent.firstChild)
+                        parent.firstChild = parent.lastChild = o;
+                     else
+                     {
+                        parent.lastChild.nextSibling = o;
+                        o.prevSibling = parent.lastChild;
+                        parent.lastChild = o;
+                     }
+                  }
+                  cache.objects.Add(o);
+#if _DEBUG
+                  if(cache.objects.count % 1000 == 0)
+                     PrintLn(path, " ----- ", cache.objects.count / 1000, " --------- ", dev);
+                  FileGetStatsLink(path, s);
+                  if(s.inode != inode)
+                  {
+                     PrintLn(s.inode);
+                     PrintLn(inode);
+                     PrintLn("crap");
+                  }
+                  p = o.GetPath();
+                  if(strcmp(p, path))
+                  {
+                     int c;
+                     PrintLn("damn");
+                     PrintLn(p);
+                     for(c = 0; c < cache.normalParentStack.count; c++)
+                     {
+                        delete p;
+                        p = cache.normalParentStack[c].GetPath();
+                        PrintLn(p);
+                     }
+                     PrintLn(path);
+                     PrintLn("bad");
+                  }
+                  delete p;
+#endif
+                  if(dev && inode && cache.bits.indexInodes)
+                  {
+                     FileSystemDeviceCache devCache = cache.deviceCaches[dev];
+                     if(!devCache)
+                        cache.deviceCaches[dev] = devCache = { };
+                     {
+                        Array<FSCacheObject> inodes = devCache.inodes[inode];
+                        if(!inodes)
+                           devCache.inodes[inode] = inodes = { };
+                        else if(inodes.count == 1)
+                           devCache.nonSingleLinks[inode] = true;
+                        inodes.Add(o);
+                     }
+                  }
+                  return result;
+               }
+
+               void /*FileSystemCache*/DummyFileSystemCacheWindow::OnEnteringDirectory(char * path)
+               {
+                  FileSystemCache cache = this.cache;
+                  FSCacheObject o = cache.objects.lastIterator.data;
+                  cache.normalParentStack.Add(o);
+               }
+
+               void /*FileSystemCache*/DummyFileSystemCacheWindow::OnLeavingDirectory(char * path)
+               {
+                  FileSystemCache cache = this.cache;
+                  cache.normalParentStack.lastIterator.Remove();
+               }
+            };
+            fsi.Iterate(path, false);
+            {
+               if(cache.objects.count)
+               {
+                  for(dev : cache.deviceCaches)
+                  {
+                     PrintLn("# of inodes (dev", &dev, "): ", dev.inodes.count);
+
+                     if(dev.nonSingleLinks.count)
+                     {
+                        for(inode : dev.nonSingleLinks)
+                        {
+                           PrintLn("   ", &inode);
+                           for(o : dev.inodes[&inode])
+                           {
+                              char * path = o.GetPath();
+                              PrintLn(path);
+                              delete path;
+                           }
+                        }
+                     }
+                  }
+               }
+            }
+            delete cache.normalParentStack;
+            delete fsi;
+         }
+      }
+      //return cache;
+   }
+
+   FSCacheObject FetchObject(char * path)
+   {
+      FSCacheObject result = null;
+      for(root : roots)
+      {
+         FSCacheObject o = &root;
+         Array<String> names = GetPathDirNamesArray(path);
+         int c, max = Min(names.count, root.names.count);
+         for(c = 0; c < max; c++)
+         {
+            if(strcmp(names[c], root.names[c]))
+               break;
+         }
+         if(c == max)
+         {
+            if(c == names.count)
+               result = o;
+            else
+            {
+               for(; c < names.count; c++)
+               {
+                  FSCacheObject child = o.firstChild;
+                  o = null;
+                  while(child)
+                  {
+                     if(!strcmp(child.name, names[c]))
+                     {
+                        o = child;
+                        break;
+                     }
+                     child = child.nextSibling;
+                  }
+                  if(!o)
+                     break;
+               }
+               if(o)
+                  result = o;
+            }
+            break;
+         }
+         delete names;
+      }
+      return result;
+   }
+
+   bool FileGetStats(char * path, FileStats stats)
+   {
+      FSCacheObject o = FetchObject(path);
+      if(o)
+         stats = o.stats;
+      else
+         stats = { };
+      return o != null;
+   }
+
+   Map<uint, Map<uint, bool>> devsInodesDone { };
+   Map<String, bool> linksPaths { };
+   void Special(char * path/*, Map<String, bool> linksPaths*//*Map<uint, Map<uint, bool>> devsInodesDone*/, DummyFileSystemCacheWindow dummyWindow)
+   {
+      FileSystemCacheIterator fsci { owner = dummyWindow, cache = this, iterateStartPath = true;
+         bool /*FileSystemCache*/DummyFileSystemCacheWindow::OnObject(char * name, char * path, FileStats stats, bool isRootObject)
+         {
+            uint dev = stats.dev;
+            uint inode = stats.inode;
+            FileSystemCache cache = this.cache;
+            if(dev && inode)
+            {
+               FileSystemDeviceCache devCache = cache.deviceCaches[dev];
+               if(devCache)
+               {
+                  Array<FSCacheObject> links = devCache.inodes[inode];
+                  if(links && links.count)
+                  {
+                     if(links.count > 1)
+                     {
+                        Map<uint, bool> inodesDone = cache.devsInodesDone[dev];
+                        Map<String, bool> linksPaths = cache.linksPaths;
+                        bool done;
+                        if(!inodesDone)
+                           cache.devsInodesDone[dev] = inodesDone = { };
+                        done = inodesDone[inode];
+                        if(!done)
+                        {
+                           inodesDone[inode] = true;
+                           //PrintLn("   ", &inode);
+                           for(o : links)
+                           {
+                              char * path = o.GetPath();
+                              //PrintLn(path);
+                              //delete path;
+                              linksPaths[path] = true;
+                           }
+                        }
+                     }
+                  }
+                  else
+                     PrintLn("no links/count!!");
+               }
+               else
+                  PrintLn("no devCache!!");
+            }
+            else
+               PrintLn("no dev/inode");
+            return true;
+         }
+      };
+      fsci.Iterate(path, false);
+      delete fsci;
+   }
+   void SpecialPrint()
+   {
+      MapNode<String, bool> mn;
+      PrintLn(" -------------------------------------------- Special -------------------------------------------- ");
+      PrintLn(" ------------------------------------------------------------------------------------------------- ");
+      for(mn = linksPaths.root.minimum; mn; mn = mn.next)
+      {
+         PrintLn(mn.key);
+      }
+   }
+
+private:
+   FileSystemCacheBits bits;
+
+   ~FileSystemCache()
+   {
+      objects.Free();
+      delete objects;
+   }
+}
+
+public class FileSystemDeviceCache : struct
+{
+public:
+   Map<uint, Array<FSCacheObject>> inodes { };
+   Map<uint, bool> nonSingleLinks { }; // using a map presuming it will be faster than Array::Find() //Array<uint> nonSingleLinks { };
+
+private:
+}
+
+class FSCacheRoot : struct
+{
+public:
+   uint position;
+   Array<String> names;
+
+private:
+   ~FSCacheRoot()
+   {
+      if(names)
+      {
+         names.Free();
+         delete names;
+      }
+   }
+}
+
+/*
+enum FSCacheObjectType { normal };
+
+class FSCacheObjectBits
+{
+   FSCacheObjectType type:4;
+   bool isRoot:1;
+}
+*/
+
+public class FSCacheObject : struct
+{
+public:
+   FSCacheObject parent;
+   char * name;
+   FileStats stats;
+   FSCacheObject firstChild;
+   FSCacheObject lastChild;
+   FSCacheObject prevSibling;
+   FSCacheObject nextSibling;
+
+   char * GetPath()
+   {
+      int c;
+      char path[MAX_LOCATION] = "";
+      FSCacheObject o;
+      Array<FSCacheObject> stack { };
+      for(o = this; o; o = o.parent)
+         stack.Add(o);
+      for(c = stack.count - 1; c >= 0; c--)
+      {
+         o = stack[c];
+         if(c < stack.count - 1)
+            strcat(path, DIR_SEPS);
+         strcat(path, o.name);
+      }
+      delete stack;
+      return CopyString(path);
+   }
+
+private:
+//   FSCacheObjectBits bits;
+
+   ~FSCacheObject()
+   {
+      delete name;
+   }
+}
+
+Array<String> GetPathDirNamesArray(char * path)
+{
+   Array<String> names;
+   if(path && path[0])
+   {
+      char * p = path;
+      char rest[MAX_LOCATION];
+      char name[MAX_FILENAME];
+      names = { };
+      while(strlen(p))
+      {
+         SplitDirectory(p, name, rest);
+         if(p == path) p = rest;
+         names.Add(CopyString(name));
+      }
+   }
+   return names;
+}