Go 1.15 から io.CopyBuffer はコピー先が *os.File だと指定したバッファーを使わない
TL;DR
Go 1.15 から io.CopyBuffer はコピー先が *os.File だと指定したバッファーを使わない
6/3 21:57 追記
調べてみたら Go 1.15 の Release Note https://t.co/VUcSeYYrZB の os の項目に説明がありました。issue もあるみたいですね https://t.co/q1mhweAV7d
— matsuyoshi (@matsuyoshi30) 2023年6月3日
既知の問題でリリースノートにも書いてありました、情報提供ありがとうございます!
Go 1.15 Release Notes - The Go Programming Language
本題
Go で io.Reader から io.Writer に対してデータをコピーしたいとき io.Copy を使うと思います。
io.Copy は io.WriterTo や io.ReaderFrom が実装されている対象に対してはそれを使います。 そうでない場合は 32 キロバイトのバッファーを使ってコピーを行います。
この 32 キロバイトのバッファーが毎回メモリ確保されるのでループなどで io.Copy を使う場合は予めバッファーを確保しておいて io.CopyBuffer を使うかなと思います。
ですが Go 1.15 からコピー先が *os.File だと io.CopyBuffer が渡したバッファーを使ってくれません。
*os.File は io.ReaderFrom が実装されているのでその分岐に入ります。
*os.File の ReadFrom の実装は以下です。
プラットフォームごとに異なる実装の readFrom に処理が移譲されています。現時点だと実装されているのは linux だけでそれ以外は stub の実装です。
linux の実装も基本的には readFrom の先が *os.File だったときに copy_file_range(2) を使うためのものです。
コピー元が *os.File じゃなければ genericReadFrom にフォールバックして io.Copy が使われてしまいます。(無限ループにならないために Write しか実装してない onlyWriter という型で包んでいる)
ReadFrom にはバッファーを受け取る口がないので渡したバッファーを考慮できません。
結論
io.CopyBuffer はコピー先が *os.File だと指定したバッファーを使いません。
バッファーを指定したい場合は Write のみを実装した onlyWriter で包んで io.CopyBuffer にわたす必要があります。