Skip to content

Commit

Permalink
Merge pull request #55 from joegilkes/iobuffer_read
Browse files Browse the repository at this point in the history
Add support for reading from IOBuffers
  • Loading branch information
jameskermode authored Nov 18, 2024
2 parents 0fa87c7 + aa6be0e commit c62f812
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 12 deletions.
4 changes: 2 additions & 2 deletions src/atoms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,9 @@ AtomsBase.species(s::Atoms, i::_IDX) =

# --------- FileIO compatible interface (hence not exported)

load(file::Union{String,IOStream}, frame; kwargs...) = Atoms(read_frame(file, frame; kwargs...))
load(file::Union{String,IOStream,IOBuffer}, frame; kwargs...) = Atoms(read_frame(file, frame; kwargs...))

function load(file::Union{String,IOStream}; kwargs...)
function load(file::Union{String,IOStream,IOBuffer}; kwargs...)
seq = Atoms.(read_frames(file; kwargs...))
if length(seq) == 1
return seq[1]
Expand Down
36 changes: 26 additions & 10 deletions src/fileio.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ function cfopen(f::Function, filename::String, mode::String="r")
end
end

function cfopen(f::Function, iob::IOBuffer, mode::String="r")
@static if Sys.iswindows()
throw(ErrorException("Reading frames from IOBuffers is not supported on Windows."))
end
fp = ccall(:fmemopen, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Cstring), pointer(iob.data, iob.ptr), iob.size, mode)
try
f(fp)
finally
cfclose(fp)
end
end

struct DictEntry
key::Ptr{Cchar}
data::Ptr{Cvoid}
Expand Down Expand Up @@ -258,7 +270,9 @@ end
read_frame(file)
Read a single frame from the ExtXYZ file `file`, which can be a file pointer,
open IO stream or a string filename.
an open IO stream, a string filename or an IOBuffer.
Reading from IOBuffers is currently not supported on Windows.
"""
function read_frame(fp::Ptr{Cvoid}; verbose=false)
nat, info, arrays = try
Expand Down Expand Up @@ -291,8 +305,8 @@ function read_frame(fp::Ptr{Cvoid}; verbose=false)
return dict
end

read_frame(file::Union{String,IOStream}, index; kwargs...) = only(read_frames(file, index; kwargs...))
read_frame(file::Union{String,IOStream}; kwargs...) = read_frame(file, 1; kwargs...)
read_frame(file::Union{String,IOStream,IOBuffer}, index; kwargs...) = only(read_frames(file, index; kwargs...))
read_frame(file::Union{String,IOStream,IOBuffer}; kwargs...) = read_frame(file, 1; kwargs...)

"""
iread_frames(file[, range])
Expand Down Expand Up @@ -327,33 +341,35 @@ function iread_frames(fp::Ptr{Cvoid}, range; close_fp=false, kwargs...)
end
end

function iread_frames(file::Union{String,IOStream}, range; kwargs...)
function iread_frames(file::Union{String,IOStream,IOBuffer}, range; kwargs...)
fp = cfopen(file, "r")
fp == C_NULL && error("file $file cannot be opened for reading")
iread_frames(fp, range; close_fp=true, kwargs...)
end

iread_frames(file::Union{String,IOStream}, index::Int; kwargs...) = iread_frames(file, [index]; kwargs...)
iread_frames(file::Union{String,IOStream}; kwargs...) = iread_frames(file, Iterators.countfrom(1); kwargs...)
iread_frames(file::Union{String,IOStream,IOBuffer}, index::Int; kwargs...) = iread_frames(file, [index]; kwargs...)
iread_frames(file::Union{String,IOStream,IOBuffer}; kwargs...) = iread_frames(file, Iterators.countfrom(1); kwargs...)

"""
read_frames(file[, range])
Read a sequence of frames from the ExtXYZ `file`, which can be specified by a file pointer, filename or IOStream.
Read a sequence of frames from the ExtXYZ `file`, which can be specified by a file pointer, filename, IOStream or IOBuffer.
`range` can be a single integer, range object or integer array of frame indices.
Reading from IOBuffers is currently not supported on Windows.
"""
read_frames(fp::Ptr{Cvoid}, range; kwargs...) = collect(iread_frames(fp, range; kwargs...))

function read_frames(file::Union{String,IOStream}, range; kwargs...)
function read_frames(file::Union{String,IOStream,IOBuffer}, range; kwargs...)
cfopen(file) do fp
fp == C_NULL && error("file $file cannot be opened for reading")
read_frames(fp, range; kwargs...)
end
end

read_frames(file::Union{String,IOStream}, index::Int; kwargs...) = read_frames(file, [index]; kwargs...)
read_frames(file::Union{String,IOStream}; kwargs...) = read_frames(file, Iterators.countfrom(1); kwargs...)
read_frames(file::Union{String,IOStream,IOBuffer}, index::Int; kwargs...) = read_frames(file, [index]; kwargs...)
read_frames(file::Union{String,IOStream,IOBuffer}; kwargs...) = read_frames(file, Iterators.countfrom(1); kwargs...)

function write_frame_dicts(fp::Ptr{Cvoid}, nat, info, arrays; verbose=false)
nat = Cint(nat)
Expand Down
8 changes: 8 additions & 0 deletions test/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ Si 13.00000000 14.00000000 $(frame+1).00000000 0
close(f)

@test all(seq1 .== seq3)

iob = IOBuffer(read(infile, String))
@static if Sys.iswindows()
@test_throws "not supported on Windows" read_frames(iob)
else
seq4 = read_frames(iob)
@test all(seq1 .== seq4)
end
end

@testset "convert" begin
Expand Down

0 comments on commit c62f812

Please sign in to comment.