summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-mmap/mmap.go
diff options
context:
space:
mode:
authorLibravatar kim <grufwub@gmail.com>2025-11-05 17:53:47 +0100
committerLibravatar tobi <tobi.smethurst@protonmail.com>2025-11-17 14:12:29 +0100
commitddf887d95c1cef95746f9881dc4180d983e7a351 (patch)
tree085dd1942c87ca1ff7da32dcdc5b3a9c0b6e5f28 /vendor/codeberg.org/gruf/go-mmap/mmap.go
parent[bugfix] more RSS validation issues (#4517) (diff)
downloadgotosocial-ddf887d95c1cef95746f9881dc4180d983e7a351.tar.xz
[performance] when transforming media, perform read operations of large files using mmap (#4541)
the performance gains aren't as substantial as i was hoping, but benchmarks did show it averaged out faster using this method. unfortunately i managed to lose the benchmarks i wrote with a poorly timed `git checkout -- .` 😭 Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4541 Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
Diffstat (limited to 'vendor/codeberg.org/gruf/go-mmap/mmap.go')
-rw-r--r--vendor/codeberg.org/gruf/go-mmap/mmap.go142
1 files changed, 142 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-mmap/mmap.go b/vendor/codeberg.org/gruf/go-mmap/mmap.go
new file mode 100644
index 000000000..797ed3e73
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-mmap/mmap.go
@@ -0,0 +1,142 @@
+package mmap
+
+import (
+ "errors"
+ "io"
+ "io/fs"
+ "runtime"
+ "syscall"
+)
+
+// MmapFile maps file at path into memory using syscall.mmap(),
+// and returns a protected MmapReader{} for accessing the mapped data.
+// Note that the mapped memory is not concurrency safe (other than
+// concurrent ReadAt() calls). Any other calls made concurrently to
+// Read() or Close() (including ReadAt()) require protection.
+func MmapFile(path string) (*MmappedFile, error) {
+
+ // Stat file information.
+ stat, err := stat(path)
+ if err != nil {
+ return nil, err
+ }
+
+ // Mmap file into memory.
+ return openMmap(path, stat)
+}
+
+func openMmap(path string, stat *fileStat) (*MmappedFile, error) {
+ if stat.Size() <= 0 {
+ // Empty file, no-op read.
+ return &MmappedFile{}, nil
+ }
+
+ // Check file data size is accessible.
+ if stat.Size() != int64(int(stat.Size())) {
+ return nil, errors.New("file is too large")
+ }
+
+ // Open file at path for read-only access.
+ fd, err := open(path, syscall.O_RDONLY, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ // Map this file into memory as slice.
+ mem, err := mmap(fd, 0, int(stat.Size()),
+ syscall.PROT_READ, syscall.MAP_PRIVATE)
+
+ // Done with file.
+ _ = close_(fd)
+
+ if err != nil {
+ return nil, err
+ }
+
+ // Return as wrapped reader type.
+ return newMmapReader(mem, stat), nil
+}
+
+// newMmapReader wraps a mapped memory slice in an
+// MmappedFile{}, also setting a GC finalizer function.
+func newMmapReader(mem []byte, stat *fileStat) *MmappedFile {
+ r := &MmappedFile{b: mem, s: stat}
+ runtime.SetFinalizer(r, (*MmappedFile).Close)
+ return r
+}
+
+type MmappedFile struct {
+ b []byte // mapped memory
+ n int // read index
+ s *fileStat // file info
+}
+
+func (r *MmappedFile) Name() string {
+ return r.s.name
+}
+
+func (r *MmappedFile) Stat() (fs.FileInfo, error) {
+ return r.s, nil
+}
+
+func (r *MmappedFile) Read(b []byte) (n int, err error) {
+ if r.n >= len(r.b) {
+ return 0, io.EOF
+ }
+ n = copy(b, r.b[r.n:])
+ r.n += n
+ return
+}
+
+func (r *MmappedFile) ReadAt(b []byte, off int64) (n int, err error) {
+ if off > int64(len(r.b)) {
+ return 0, io.EOF
+ }
+ n = copy(b, r.b[off:])
+ return n, nil
+}
+
+func (r *MmappedFile) WriteTo(w io.Writer) (int64, error) {
+ if r.n >= len(r.b) {
+ return 0, io.EOF
+ }
+ n, err := w.Write(r.b[r.n:])
+ r.n += n
+ return int64(n), err
+}
+
+func (r *MmappedFile) Seek(off int64, whence int) (int64, error) {
+ var n int
+ switch whence {
+ case io.SeekCurrent:
+ n = r.n + int(off)
+ case io.SeekStart:
+ n = 0 + int(off)
+ case io.SeekEnd:
+ n = len(r.b) + int(off)
+ default:
+ return 0, errors.New("invalid argument")
+ }
+ if n < 0 || n > len(r.b) {
+ return 0, errors.New("invalid argument")
+ }
+ r.n = n
+ return int64(n), nil
+}
+
+func (r *MmappedFile) Len() int {
+ return len(r.b) - r.n
+}
+
+func (r *MmappedFile) Size() int64 {
+ return int64(len(r.b))
+}
+
+func (r *MmappedFile) Close() error {
+ if b := r.b; b != nil {
+ r.b = nil
+ runtime.SetFinalizer(r, nil)
+ return munmap(b)
+ }
+ return nil
+}