]> Cypherpunks.ru repositories - goredo.git/blobdiff - depfix.go
Binary format and many optimisations
[goredo.git] / depfix.go
index bc4452cef820c3a40b507bd860ec9094be701d5a..1d1bdea13355334d4ec3c34ae5e2a145802d040a 100644 (file)
--- a/depfix.go
+++ b/depfix.go
@@ -18,6 +18,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
+       "bufio"
+       "bytes"
        "errors"
        "io"
        "io/fs"
@@ -25,9 +27,23 @@ import (
        "path"
        "strings"
 
+       "github.com/google/uuid"
        "go.cypherpunks.ru/recfile"
 )
 
+var DepFixHashCache map[string]Hash
+
+func recfileWrite(fdDep io.StringWriter, fields ...recfile.Field) error {
+       w := recfile.NewWriter(fdDep)
+       if _, err := w.RecordStart(); err != nil {
+               return err
+       }
+       if _, err := w.WriteFields(fields...); err != nil {
+               return err
+       }
+       return nil
+}
+
 func depFix(root string) error {
        tracef(CDebug, "depfix: entering %s", root)
        dir, err := os.Open(root)
@@ -62,7 +78,6 @@ func depFix(root string) error {
                return ErrLine(err)
        }
        defer dir.Close()
-       redoDirChanged := false
        for {
                entries, err := dir.ReadDir(1 << 10)
                if err != nil {
@@ -72,129 +87,195 @@ func depFix(root string) error {
                        return ErrLine(err)
                }
                for _, entry := range entries {
-                       if !strings.HasSuffix(entry.Name(), DepSuffix) {
+                       switch path.Ext(entry.Name()) {
+                       case DepSuffix:
+                       case ".rec":
+                       default:
                                continue
                        }
                        tracef(CDebug, "depfix: checking %s/%s", root, entry.Name())
                        fdDepPath := path.Join(redoDir, entry.Name())
-                       fdDep, err := os.Open(fdDepPath)
+                       data, err := os.ReadFile(fdDepPath)
                        if err != nil {
                                return ErrLine(err)
                        }
-                       defer fdDep.Close()
-                       r := recfile.NewReader(fdDep)
-                       var fieldses [][]recfile.Field
-                       depChanged := false
-                       for {
-                               fields, err := r.Next()
-                               if err != nil {
-                                       if errors.Is(err, io.EOF) {
-                                               break
-                                       }
-                                       return ErrLine(err)
-                               }
-                               fieldses = append(fieldses, fields)
-                               m := make(map[string]string, len(fields))
-                               for _, f := range fields {
-                                       m[f.Name] = f.Value
-                               }
-                               if m["Type"] != DepTypeIfchange {
-                                       continue
-                               }
-                               dep := m["Target"]
-                               if dep == "" {
-                                       return ErrMissingTarget
-                               }
-                               tracef(CDebug, "depfix: checking %s/%s -> %s", root, entry.Name(), dep)
-                               theirInode, err := inodeFromRec(m)
-                               if err != nil {
+                       fdDep, err := tempfile(redoDir, entry.Name())
+                       if err != nil {
+                               return ErrLine(err)
+                       }
+                       defer os.Remove(fdDep.Name())
+                       tracef(
+                               CDebug, "depfix: %s/%s: tmp %s",
+                               root, entry.Name(), fdDep.Name(),
+                       )
+                       fdDepW := bufio.NewWriter(fdDep)
+                       switch path.Ext(entry.Name()) {
+                       case DepSuffix:
+                               if _, err = depParse(NewTgt(""), data); err != nil {
                                        return ErrLine(err)
                                }
-                               theirHsh := mustHashDecode(m["Hash"])
-                               fd, err := os.Open(path.Join(root, dep))
+                               build, data, err := depHeadParse(data)
                                if err != nil {
-                                       if errors.Is(err, fs.ErrNotExist) {
-                                               tracef(
-                                                       CDebug, "depfix: %s/%s -> %s: not exists",
-                                                       root, entry.Name(), dep,
-                                               )
-                                               continue
-                                       }
                                        return ErrLine(err)
                                }
-                               inode, _, err := inodeFromFileByFd(fd)
-                               if err != nil {
-                                       fd.Close()
+                               if err = depBuildWrite(fdDepW, build); err != nil {
                                        return ErrLine(err)
                                }
-                               if inode.Size != theirInode.Size {
-                                       tracef(
-                                               CDebug, "depfix: %s/%s -> %s: size differs",
-                                               root, entry.Name(), dep,
-                                       )
-                                       fd.Close()
-                                       continue
+                               var typ byte
+                               var chunk []byte
+                               for len(data) > 0 {
+                                       typ, chunk, data, _ = chunkRead(data)
+                                       switch typ {
+                                       case DepTypeAlways:
+                                               err = always(fdDepW, fdDep.Name())
+                                       case DepTypeStamp:
+                                               p := mustAbs(path.Join(root,
+                                                       strings.TrimSuffix(entry.Name(), DepSuffix)))
+                                               hsh, ok := DepFixHashCache[p]
+                                               if !ok {
+                                                       var fd *os.File
+                                                       fd, err = os.Open(p)
+                                                       if err != nil {
+                                                               break
+                                                       }
+                                                       hsh, err = fileHash(fd)
+                                                       fd.Close()
+                                                       if err != nil {
+                                                               break
+                                                       }
+                                                       DepFixHashCache[p] = hsh
+                                               }
+                                               err = stamp(fdDepW, fdDep.Name(), hsh)
+                                       case DepTypeIfcreate:
+                                               err = ifcreate(fdDepW, fdDep.Name(), string(chunk))
+                                       case DepTypeIfchange:
+                                               name := string(chunk[InodeLen+HashLen:])
+                                               p := mustAbs(path.Join(root, name))
+                                               var fd *os.File
+                                               fd, err = os.Open(p)
+                                               if err != nil {
+                                                       break
+                                               }
+                                               var inode Inode
+                                               inode, _, err = inodeFromFileByFd(fd)
+                                               if err != nil {
+                                                       fd.Close()
+                                                       break
+                                               }
+                                               hsh, ok := DepFixHashCache[p]
+                                               if !ok {
+                                                       hsh, err = fileHash(fd)
+                                                       if err != nil {
+                                                               break
+                                                       }
+                                                       DepFixHashCache[p] = hsh
+                                               }
+                                               fd.Close()
+                                               _, err = io.Copy(fdDepW, bytes.NewBuffer(
+                                                       chunkWrite(bytes.Join([][]byte{
+                                                               {DepTypeIfchange},
+                                                               []byte(inode),
+                                                               []byte(hsh),
+                                                               []byte(name),
+                                                       }, nil))))
+                                       case DepTypeIfchangeDummy:
+                                               err = depWriteDummy(fdDepW, fdDep.Name(), string(chunk))
+                                       }
+                                       if err != nil {
+                                               return ErrLine(err)
+                                       }
                                }
-                               if inode.Equals(theirInode) {
-                                       tracef(
-                                               CDebug, "depfix: %s/%s -> %s: inode is equal",
-                                               root, entry.Name(), dep,
-                                       )
-                                       fd.Close()
-                                       continue
+                       case ".rec":
+                               defer os.Remove(fdDepPath)
+                               fdDepPath = fdDepPath[:len(fdDepPath)-4] + DepSuffix
+                               r := recfile.NewReader(bytes.NewReader(data))
+                               m, err := r.NextMap()
+                               if err != nil {
+                                       return err
                                }
-                               hsh, err := fileHash(fd)
-                               fd.Close()
+                               var build uuid.UUID
+                               build, err = uuid.Parse(m["Build"])
                                if err != nil {
-                                       return ErrLine(err)
+                                       break
                                }
-                               if hsh != theirHsh {
-                                       tracef(
-                                               CDebug, "depfix: %s/%s -> %s: hash differs",
-                                               root, entry.Name(), dep,
-                                       )
-                                       continue
+                               if err = depBuildWrite(fdDepW, build); err != nil {
+                                       return ErrLine(err)
                                }
-                               fields = []recfile.Field{
-                                       {Name: "Type", Value: DepTypeIfchange},
-                                       {Name: "Target", Value: dep},
-                                       {Name: "Hash", Value: hsh.String()},
+                               for {
+                                       m, err := r.NextMap()
+                                       if err != nil {
+                                               if errors.Is(err, io.EOF) {
+                                                       break
+                                               }
+                                               return ErrLine(err)
+                                       }
+                                       switch m["Type"] {
+                                       case "always":
+                                               err = always(fdDepW, m["Target"])
+                                       case "stamp":
+                                               p := mustAbs(path.Join(root,
+                                                       strings.TrimSuffix(entry.Name(), DepSuffix)))
+                                               hsh, ok := DepFixHashCache[p]
+                                               if !ok {
+                                                       var fd *os.File
+                                                       fd, err = os.Open(p)
+                                                       if err != nil {
+                                                               break
+                                                       }
+                                                       hsh, err = fileHash(fd)
+                                                       fd.Close()
+                                                       if err != nil {
+                                                               break
+                                                       }
+                                                       DepFixHashCache[p] = hsh
+                                               }
+                                               err = stamp(fdDepW, fdDep.Name(), hsh)
+                                       case "ifcreate":
+                                               err = ifcreate(fdDepW, fdDep.Name(), m["Target"])
+                                       case "ifchange":
+                                               if m["Size"] == "" {
+                                                       err = depWriteDummy(fdDepW, fdDep.Name(), m["Target"])
+                                                       break
+                                               }
+                                               name := string(m["Target"])
+                                               p := mustAbs(path.Join(root, name))
+                                               var fd *os.File
+                                               fd, err = os.Open(p)
+                                               if err != nil {
+                                                       break
+                                               }
+                                               var inode Inode
+                                               inode, _, err = inodeFromFileByFd(fd)
+                                               if err != nil {
+                                                       fd.Close()
+                                                       break
+                                               }
+                                               hsh, ok := DepFixHashCache[p]
+                                               if !ok {
+                                                       hsh, err = fileHash(fd)
+                                                       if err != nil {
+                                                               break
+                                                       }
+                                                       DepFixHashCache[p] = hsh
+                                               }
+                                               fd.Close()
+                                               _, err = io.Copy(fdDepW, bytes.NewBuffer(
+                                                       chunkWrite(bytes.Join([][]byte{
+                                                               {DepTypeIfchange},
+                                                               []byte(inode),
+                                                               []byte(hsh),
+                                                               []byte(name),
+                                                       }, nil))))
+                                       }
+                                       if err != nil {
+                                               return ErrLine(err)
+                                       }
                                }
-                               fields = append(fields, inode.RecfileFields()...)
-                               fieldses[len(fieldses)-1] = fields
-                               tracef(
-                                       CDebug, "depfix: %s/%s -> %s: inode updated",
-                                       root, entry.Name(), dep,
-                               )
-                               depChanged = true
-                       }
-                       fdDep.Close()
-                       if !depChanged {
-                               continue
                        }
-                       redoDirChanged = true
-                       fdDep, err = tempfile(redoDir, entry.Name())
+                       err = fdDepW.Flush()
                        if err != nil {
                                return ErrLine(err)
                        }
-                       defer fdDep.Close()
-                       tracef(
-                               CDebug, "depfix: %s/%s: tmp %s",
-                               root, entry.Name(), fdDep.Name(),
-                       )
-                       w := recfile.NewWriter(fdDep)
-                       if _, err := w.WriteFields(fieldses[0]...); err != nil {
-                               return ErrLine(err)
-                       }
-                       fieldses = fieldses[1:]
-                       for _, fields := range fieldses {
-                               if _, err := w.RecordStart(); err != nil {
-                                       return ErrLine(err)
-                               }
-                               if _, err := w.WriteFields(fields...); err != nil {
-                                       return ErrLine(err)
-                               }
-                       }
                        if !NoSync {
                                if err = fdDep.Sync(); err != nil {
                                        return ErrLine(err)
@@ -206,10 +287,10 @@ func depFix(root string) error {
                        }
                        tracef(CRedo, "%s", fdDepPath)
                }
-       }
-       if redoDirChanged && !NoSync {
-               if err = syncDir(redoDir); err != nil {
-                       return err
+               if !NoSync {
+                       if err = syncDir(redoDir); err != nil {
+                               return err
+                       }
                }
        }
        return nil