package rosa import ( "encoding/binary" "errors" "fmt" "io" "os" "runtime" "runtime/debug" "strconv" "sync" "syscall" "unique" "unsafe" "hakurei.app/internal/pkg" "hakurei.app/message" ) // wordSize is the boundary which binary segments are always aligned to. const wordSize = 8 // padSize returns the padding size for aligning sz. func padSize[T int | int64](sz T) T { return (wordSize - (sz)%wordSize) % wordSize } // WriteReport writes a report of all available [PArtifact] to w. func WriteReport(msg message.Msg, w io.Writer, c *pkg.Cache) error { var ( zero [wordSize]byte buf [len(pkg.ID{}) + wordSize]byte ) for i := range PresetEnd { a := Std.Load(PArtifact(i)) if _, ok := a.(pkg.FileArtifact); ok { msg.Verbosef("skipping file artifact %s", artifactsM[i].Name) continue } id := c.Ident(a) var f *os.File if r, err := c.OpenStatus(a); err != nil { if errors.Is(err, os.ErrNotExist) { msg.Verbosef("artifact %s unavailable", artifactsM[i].Name) continue } return err } else { f = r.(*os.File) } msg.Verbosef("writing artifact %s...", artifactsM[i].Name) var sz int64 if fi, err := f.Stat(); err != nil { _ = f.Close() return err } else { sz = fi.Size() } *(*pkg.ID)(buf[:]) = id.Value() binary.LittleEndian.PutUint64(buf[len(pkg.ID{}):], uint64(sz)) if _, err := w.Write(buf[:]); err != nil { _ = f.Close() return err } if n, err := io.Copy(w, f); err != nil { _ = f.Close() return err } else if n != sz { _ = f.Close() return fmt.Errorf("strange status file copy: %d != %d", n, sz) } else if err = f.Close(); err != nil { return err } if psz := padSize(sz); psz > 0 { if _, err := w.Write(zero[:psz]); err != nil { return err } } // existence of status implies cured artifact var n int if pathname, _, err := c.Cure(a); err != nil { return err } else if n, err = pkg.Flatten( os.DirFS(pathname.String()), ".", io.Discard, ); err != nil { return err } binary.LittleEndian.PutUint64(buf[:], uint64(n)) if _, err := w.Write(buf[:wordSize]); err != nil { return err } } return nil } // Report provides efficient access to a report file populated by [WriteReport]. type Report struct { // Underlying file, must not be exposed directly. f *os.File // Slice backed by f. Access must be prepared by HandleAccess. data []byte // Offsets into data for each identifier. offsets map[unique.Handle[pkg.ID]]int // Outcome of a call to Close. closeErr error // Synchronises calls to Close. closeOnce sync.Once } // OpenReport opens a file populated by [WriteReport] func OpenReport(pathname string) (rp *Report, err error) { var r Report if r.f, err = os.Open(pathname); err != nil { return } defer func() { if err != nil { _ = r.f.Close() if r.data != nil { _ = syscall.Munmap(r.data) } } }() var fi os.FileInfo if fi, err = r.f.Stat(); err != nil { return } if r.data, err = syscall.Mmap( int(r.f.Fd()), 0, int(fi.Size()), syscall.PROT_READ, syscall.MAP_PRIVATE, ); err != nil { return } defer r.HandleAccess(&err)() var offset int r.offsets = make(map[unique.Handle[pkg.ID]]int) for offset < len(r.data) { id := unique.Make((pkg.ID)(r.data[offset:])) offset += len(pkg.ID{}) r.offsets[id] = offset offset += int(binary.LittleEndian.Uint64(r.data[offset:])) + wordSize offset += padSize(offset) offset += wordSize } return &r, nil } // ReportIOError describes an I/O error while accessing a [Report]. type ReportIOError struct { Offset int Err error } // Unwrap returns the underlying runtime error. func (e *ReportIOError) Unwrap() error { return e.Err } // Error returns a description of the error offset. func (e *ReportIOError) Error() string { return "report I/O error at offset " + strconv.Itoa(e.Offset) } // HandleAccess prepares for accessing memory returned by a method of [Report] // and returns a function that must be deferred by the caller. func (r *Report) HandleAccess(errP *error) func() { pof := debug.SetPanicOnFault(true) return func() { debug.SetPanicOnFault(pof) v := recover() if v == nil { return } if err, ok := v.(error); !ok { panic(v) } else if *errP != nil { return } else { *errP = err } var runtimeError interface { Addr() uintptr runtime.Error } if errors.As(*errP, &runtimeError) { offset := int(runtimeError.Addr() - uintptr(unsafe.Pointer(unsafe.SliceData(r.data)))) // best effort for fragile uintptr if offset >= 0 { *errP = &ReportIOError{offset, *errP} } } } } // ArtifactOf returns information of a [pkg.Artifact] corresponding to id. func (r *Report) ArtifactOf(id unique.Handle[pkg.ID]) (status []byte, n int64) { if offset, ok := r.offsets[id]; !ok { n = -1 } else { sz := int(binary.LittleEndian.Uint64(r.data[offset:])) offset += wordSize status = r.data[offset : offset+sz] offset += sz + padSize(sz) n = int64(binary.LittleEndian.Uint64(r.data[offset:])) } return } // Close closes the underlying file and releases all associated resources. func (r *Report) Close() error { r.closeOnce.Do(func() { err := syscall.Munmap(r.data) r.closeErr = errors.Join(err, r.f.Close()) }) return r.closeErr }