Skip to content

Commit

Permalink
ReadOnlyBuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
JonasIsensee committed Jan 16, 2024
1 parent 15eaa50 commit 80ebabf
Show file tree
Hide file tree
Showing 3 changed files with 206 additions and 5 deletions.
9 changes: 6 additions & 3 deletions src/JLD2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,9 @@ mutable struct JLDFile{T<:IO}
JLDWriteSession(), Dict{String,Any}(), IdDict(), IdDict(), Dict{RelOffset,WeakRef}(),
DATA_START, Dict{RelOffset,GlobalHeap}(),
GlobalHeap(0, 0, 0, Int64[]), Dict{RelOffset,Group{JLDFile{T}}}(), UNDEFINED_ADDRESS)
finalizer(jld_finalizer, f)
if !(io isa ReadOnlyBuffer)
finalizer(jld_finalizer, f)
end
f
end
end
Expand Down Expand Up @@ -438,9 +440,9 @@ function load_file_metadata!(f)
end

"""
jldopen(fname::AbstractString, mode::AbstractString; iotype=MmapIO, compress=false, typemap=Dict())
jldopen(file, mode::AbstractString; iotype=MmapIO, compress=false, typemap=Dict())
Opens a JLD2 file at path `fname`.
Opens a JLD2 file at path `file`. Alternatively `file` may be a suitable IO object.
`"r"`: Open for reading only, failing if no file exists
`"r+"`: Open for reading and writing, failing if no file exists
Expand Down Expand Up @@ -623,6 +625,7 @@ include("data/custom_serialization.jl")
include("data/writing_datatypes.jl")
include("data/reconstructing_datatypes.jl")

include("general_io.jl")
include("dataio.jl")
include("loadsave.jl")
include("stdlib.jl")
Expand Down
4 changes: 2 additions & 2 deletions src/dataio.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ function read_array!(v::Array{T}, f::JLDFile{MmapIO},
v
end

function read_array!(v::Array{T}, f::JLDFile{MmapIO},
rr::ReadRepresentation{T,RR}) where {T,RR}
function read_array!(v::Array{T}, f::JLDFile{IO},
rr::ReadRepresentation{T,RR}) where {T,RR, IO<:Union{MmapIO,ReadOnlyBuffer}}
io = f.io
inptr = io.curptr
n = length(v)
Expand Down
198 changes: 198 additions & 0 deletions src/general_io.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
## Requirements for an IO object
#=
Implement for Reading:
Base.position(io)::Int
Base.close(io)
Base.seek(io, pos::Int)
read_bytestring(io)::String # read a null-terminated byte string
Base.skip(io, n::Int)
jlread(io, ::Type{T}) for [U]Int8 and `PlainType`
jlread(io, ::Type{T}, n::Integer) read n elements of type T into a vector
Base.read!(io, vec::Vector{UInt8})
begin_checksum_read(io) # see existing implementations
end_checksum(io)
read_scalar(f::JLDFile{<:CustomIOType}, rr, header_offset)
read_array!(v::Array{T}, f::JLDFile{<:CustomIOType}, rr::ReadRepresentation{T,T}) where T
read_array!(v::Array{T}, f::JLDFile{<:CustomIOType}, rr::ReadRepresentation{T,RR}) where {T,RR}
=#



## Create an IO object which wraps a non-seekable read-only buffer
const MINBUFFERSIZE = 2^9 # should maybe be 2^16

mutable struct ReadOnlyBuffer{B <: IO} <: IO
_buf::B
offset::UInt64 # position of file start in wrapped stream
data::Vector{UInt8}
startptr::Ptr{Cvoid}
curptr::Ptr{Cvoid}
endptr::Ptr{Cvoid}
size::UInt64
checksum_pos::Vector{Int64}
nchecksum::Int64
function ReadOnlyBuffer(_buf::IO)
offset = position(_buf)
nb = min(MINBUFFERSIZE, bytesavailable(_buf))
data = read(_buf, nb)
startptr = pointer(data)
curptr = startptr
endptr = startptr + nb-1
new{typeof(_buf)}(_buf, offset, data,

Check warning on line 44 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L37-L44

Added lines #L37 - L44 were not covered by tests
startptr, curptr, endptr,
length(data), Int[], 0)
end
end

Base.position(io::ReadOnlyBuffer) = Int(io.curptr-io.startptr)

Check warning on line 50 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L50

Added line #L50 was not covered by tests

Base.close(::ReadOnlyBuffer) = nothing

Check warning on line 52 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L52

Added line #L52 was not covered by tests

function Base.seek(io::ReadOnlyBuffer, n::Integer)
n > io.size && resize!(io, n)
io.curptr = io.startptr+n

Check warning on line 56 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L54-L56

Added lines #L54 - L56 were not covered by tests
end

function Base.resize!(io::ReadOnlyBuffer, newend::Integer)
newend < io.size && return
readmore!(io, newend-io.size)
if !(newend io.size)
throw(EOFError())

Check warning on line 63 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L59-L63

Added lines #L59 - L63 were not covered by tests
end
end

function readmore!(io::ReadOnlyBuffer, nb::Integer=MINBUFFERSIZE)
nb = min(bytesavailable(io._buf), max(nb, MINBUFFERSIZE))
bts = read(io._buf, nb)
append!(io.data, bts)
io.size += length(bts)
_updatepointers!(io)

Check warning on line 72 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L67-L72

Added lines #L67 - L72 were not covered by tests
end

# Read a null-terminated string
function read_bytestring(io::ReadOnlyBuffer)
nb = 0
while true
idx = position(io)+1+nb
idx > io.size && resize!(io, idx)
io.data[idx] == 0x00 && break
nb += 1
end
pos = position(io)
v = io.data[pos+1 : pos+1+nb]
skip(io, nb+1)
return String(v)

Check warning on line 87 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L76-L87

Added lines #L76 - L87 were not covered by tests
end

function Base.skip(io::ReadOnlyBuffer, offset::Integer)
if io.curptr+offset > io.endptr
resize!(io, position(io)+offset)

Check warning on line 92 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L90-L92

Added lines #L90 - L92 were not covered by tests
end
io.curptr += offset
nothing

Check warning on line 95 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L94-L95

Added lines #L94 - L95 were not covered by tests
end

function _read(io::ReadOnlyBuffer, T::DataType)
ep = io.curptr + jlsizeof(T)
ep > io.endptr && resize!(io, ep)
v = jlunsafe_load(Ptr{T}(io.curptr))
io.curptr += jlsizeof(T)
v

Check warning on line 103 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L98-L103

Added lines #L98 - L103 were not covered by tests
end
jlread(io::ReadOnlyBuffer, T::Type{UInt8}) = _read(io, T)
jlread(io::ReadOnlyBuffer, T::Type{Int8}) = _read(io, T)
jlread(io::ReadOnlyBuffer, T::PlainType) = _read(io, T)

Check warning on line 107 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L105-L107

Added lines #L105 - L107 were not covered by tests

function jlread(io::ReadOnlyBuffer, ::Type{T}, n::Int) where T
if io.endptr < io.curptr + jlsizeof(T)*n
readmore!(io, jlsizeof(T)*n)

Check warning on line 111 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L109-L111

Added lines #L109 - L111 were not covered by tests
end
arr = Vector{T}(undef, n)
unsafe_copyto!(pointer(arr), Ptr{T}(io.curptr), n)
io.curptr += jlsizeof(T)*n
arr

Check warning on line 116 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L113-L116

Added lines #L113 - L116 were not covered by tests
end
jlread(io::ReadOnlyBuffer, ::Type{T}, n::Integer) where {T} = jlread(io, T, Int(n))

Check warning on line 118 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L118

Added line #L118 was not covered by tests

function Base.read!(io::ReadOnlyBuffer, vec::Vector{UInt8})
nb = length(vec)
position(io)+nb > io.size && resize!(io, newpos)
unsafe_copyto!(pointer(vec), Ptr{UInt8}(io.curptr), nb)
io.curptr += nb
vec

Check warning on line 125 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L120-L125

Added lines #L120 - L125 were not covered by tests
end

# We sometimes need to compute checksums. We do this by first calling begin_checksum when
# starting to handle whatever needs checksumming, and calling end_checksum afterwards. Note
# that we never compute nested checksums, but we may compute multiple checksums
# simultaneously. This strategy is not thread-safe.

function begin_checksum_read(io::ReadOnlyBuffer)
idx = io.nchecksum += 1
if idx > length(io.checksum_pos)
push!(io.checksum_pos, position(io))

Check warning on line 136 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L133-L136

Added lines #L133 - L136 were not covered by tests
else
io.checksum_pos[idx] = position(io)

Check warning on line 138 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L138

Added line #L138 was not covered by tests
end
io

Check warning on line 140 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L140

Added line #L140 was not covered by tests
end

function end_checksum(io::ReadOnlyBuffer)
v = io.checksum_pos[io.nchecksum]
io.nchecksum -= 1
Lookup3.hash(Ptr{UInt8}(io.startptr + v), position(io) - v)

Check warning on line 146 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L143-L146

Added lines #L143 - L146 were not covered by tests
end

###########################################################################################
## API

function jldopen(io, cr::Bool, create::Bool, truncate::Bool, iotype;

Check warning on line 152 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L152

Added line #L152 was not covered by tests
compress=false,
typemap::Dict{String}=Dict{String,Any}(),
)

verify_compressor(compress)

Check warning on line 157 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L157

Added line #L157 was not covered by tests

# figure out what kind of io object this is
# for now assume it is
if !io.readable
throw("IO object is not readable")

Check warning on line 162 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L161-L162

Added lines #L161 - L162 were not covered by tests
end
if io.seekable

Check warning on line 164 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L164

Added line #L164 was not covered by tests
# Here could have a more lightweight wrapper
# that just ensures API is defined
end
if (false == cr == create == truncate)

Check warning on line 168 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L167-L168

Added lines #L167 - L168 were not covered by tests
# Were trying to read, so let's hope `io` implements `read`
# and bytesavailable
io = ReadOnlyBuffer(io)
f = JLDFile(io, "ReadOnlyBuffer", false, true, compress, false)
load_file_metadata!(f)
merge!(f.typemap, typemap)
return f

Check warning on line 175 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L171-L175

Added lines #L171 - L175 were not covered by tests
end
end


function read_scalar(f::JLDFile{<:ReadOnlyBuffer}, @nospecialize(rr), header_offset::RelOffset)::Any
io = f.io
inptr = io.curptr
obj = jlconvert(rr, f, inptr, header_offset)
io.curptr = inptr + odr_sizeof(rr)
obj

Check warning on line 185 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L180-L185

Added lines #L180 - L185 were not covered by tests
end


function read_array!(v::Array{T}, f::JLDFile{<:ReadOnlyBuffer},

Check warning on line 189 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L189

Added line #L189 was not covered by tests
rr::ReadRepresentation{T,T}) where T
io = f.io
inptr = io.curptr
n = length(v)
unsafe_copyto!(pointer(v), pconvert(Ptr{T}, inptr), n)
io.curptr = inptr + odr_sizeof(T) * n
v

Check warning on line 196 in src/general_io.jl

View check run for this annotation

Codecov / codecov/patch

src/general_io.jl#L191-L196

Added lines #L191 - L196 were not covered by tests
end

0 comments on commit 80ebabf

Please sign in to comment.