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..35a2b54de0 --- /dev/null +++ b/lib_nonwindows.go @@ -0,0 +1,37 @@ +//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() uintptr + Stat() (os.FileInfo, error) + }); ok { + if fi, err := fder.Stat(); err == nil { + if b, err := syscall.Mmap( + int(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) +}