From 45a2e309b068e165a3a8d806eaedfad7d2b17e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Gul=C3=A1csi?= Date: Sun, 27 Apr 2025 11:04:12 +0200 Subject: [PATCH 1/2] Use mmap in ReadAll if possible --- excelize.go | 2 +- lib.go | 2 +- lib_nonwindows.go | 35 +++++++++++++++++++++++++++++++++++ lib_windows.go | 16 ++++++++++++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 lib_nonwindows.go create mode 100644 lib_windows.go diff --git a/excelize.go b/excelize.go index 61bb6d3489..71d721a3fe 100644 --- a/excelize.go +++ b/excelize.go @@ -176,7 +176,7 @@ func (f *File) checkOpenReaderOptions() error { // OpenReader read data stream from io.Reader and return a populated // spreadsheet file. func OpenReader(r io.Reader, opts ...Options) (*File, error) { - b, err := io.ReadAll(r) + b, err := readAll(r) if err != nil { return nil, err } diff --git a/lib.go b/lib.go index 113d574fbf..01588a869f 100644 --- a/lib.go +++ b/lib.go @@ -118,7 +118,7 @@ func (f *File) readBytes(name string) []byte { if err != nil { return content } - content, _ = io.ReadAll(file) + content, _ = readAll(file) f.Pkg.Store(name, content) _ = file.Close() return content diff --git a/lib_nonwindows.go b/lib_nonwindows.go new file mode 100644 index 0000000000..7dbcadc79b --- /dev/null +++ b/lib_nonwindows.go @@ -0,0 +1,35 @@ +//go:build !windows + +// Copyright 2025 The excelize Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +package excelize + +import ( + "io" + "os" + "runtime" + "syscall" +) + +// readAll is like io.ReadAll, but uses mmap if possible. +func readAll(r io.Reader) ([]byte, error) { + if fder, ok := r.(interface { + Fd() int + Stat() (os.FileInfo, error) + }); ok { + if fi, err := fder.Stat(); err == nil { + if b, err := syscall.Mmap(fder.Fd(), 0, int(fi.Size()), + syscall.PROT_READ, + syscall.MAP_PRIVATE|syscall.MAP_POPULATE, + ); err == nil { + runtime.SetFinalizer(&b, func(_ any) error { + return syscall.Munmap(b) + }) + return b, nil + } + } + } + return io.ReadAll(r) +} diff --git a/lib_windows.go b/lib_windows.go new file mode 100644 index 0000000000..aca1bdefb0 --- /dev/null +++ b/lib_windows.go @@ -0,0 +1,16 @@ +//go:build windows + +// Copyright 2025 The excelize Authors. All rights reserved. Use of +// this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +package excelize + +import ( + "io" +) + +// readAll is like io.ReadAll, but uses mmap if possible. +func readAll(r io.Reader) ([]byte, error) { + return io.ReadAll(r) +} From 3da8b58f5fb466da55189064a478aa6e0563f7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20Gul=C3=A1csi?= Date: Tue, 29 Apr 2025 17:24:22 +0200 Subject: [PATCH 2/2] Fd() uintptr os.File.Fd() never returned int... --- lib_nonwindows.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib_nonwindows.go b/lib_nonwindows.go index 7dbcadc79b..35a2b54de0 100644 --- a/lib_nonwindows.go +++ b/lib_nonwindows.go @@ -16,11 +16,13 @@ import ( // readAll is like io.ReadAll, but uses mmap if possible. func readAll(r io.Reader) ([]byte, error) { if fder, ok := r.(interface { - Fd() int + Fd() uintptr Stat() (os.FileInfo, error) }); ok { if fi, err := fder.Stat(); err == nil { - if b, err := syscall.Mmap(fder.Fd(), 0, int(fi.Size()), + if b, err := syscall.Mmap( + int(fder.Fd()), + 0, int(fi.Size()), syscall.PROT_READ, syscall.MAP_PRIVATE|syscall.MAP_POPULATE, ); err == nil {