defmodule FileReader do @moduledoc """ Helper module to accelerate linear file reads. Reads next `buffer_size` bytes and keeps in state. """ defstruct [:fp, :module, :buffer_size, :offset, :chunk] def new(fp, loc, opts \\ []) do module = Keyword.get(opts, :module, :file) buffer_size = Keyword.get(opts, :buffer_size, 64_000) %FileReader{ fp: fp, module: module, buffer_size: buffer_size, offset: loc, chunk: "" } end def read(state = %FileReader{buffer_size: buffer_size, chunk: chunk}, n) when is_integer(n) and n < buffer_size do if byte_size(chunk) < n do case read_chunk(state) do :eof -> :eof state -> read(state, n) end else ret = binary_part(chunk, 0, n) chunk = binary_part(chunk, n, byte_size(chunk) - n) {%FileReader{state | chunk: chunk}, ret} end end def read(state = %FileReader{fp: fp, module: module, offset: offset, chunk: chunk}, n) when is_integer(n) do case module.pread(fp, offset, n - byte_size(chunk)) do {:ok, binary} -> {%FileReader{state | offset: offset + byte_size(binary), chunk: ""}, chunk <> binary} :eof -> :eof end end defp read_chunk( state = %FileReader{ fp: fp, module: module, buffer_size: buffer_size, chunk: chunk, offset: offset } ) do case module.pread(fp, offset, buffer_size) do {:ok, binary} -> %FileReader{state | chunk: chunk <> binary, offset: offset + byte_size(binary)} :eof -> :eof end end end