Skip to content

Commit

Permalink
fixed bug with string data io
Browse files Browse the repository at this point in the history
  • Loading branch information
m-wells committed Feb 12, 2020
1 parent abded1f commit 6f134a4
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 135 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "AlignedBinaryFormat"
uuid = "94fc9360-1a5e-4d84-93be-ddbadb32b3a7"
authors = ["m-wells <[email protected]>"]
version = "0.4.0"
version = "0.4.1"

[deps]
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
Expand Down
92 changes: 38 additions & 54 deletions src/AlignedBinaryFormat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,30 @@ using Mmap

export abfopen

#---------------------------------------------------------------------------------------------------

struct ReadOnlyError <: Exception
io::IOStream
end

function Base.showerror(io::IO, e::ReadOnlyError)
print(io, "isreadable(", e.io.name, ") = ", isreadable(io))
end

check_readable(io::IOStream) = isreadable(io) || throw(ReadOnlyError(io))

struct WriteOnlyError <: Exception
io::IOStream
end

function Base.showerror(io::IO, e::WriteOnlyError)
print(io, "iswritable(", e.io.name, ") = ", iswritable(io))
end

check_writable(io::IOStream) = iswritable(io) || throw(WriteOnlyError(io))

#---------------------------------------------------------------------------------------------------

function ImmutableDict(x::Pair, ys::Vararg{Pair})
d = Base.ImmutableDict(x)
for y in ys
Expand Down Expand Up @@ -41,52 +65,7 @@ const ARRAYLOOKUP = ImmutableDict(Base.ImmutableDict{String,Any}(),
string(BitArray) => BitArray,
string(String) => String)

function write_str(io::IOStream, str::Vararg{String})
for s in str
write(io, Int64(length(s)))
write(io, s)
end
nothing
end

function read_str(io::IOStream, n::Int)
k = Vector{Char}(undef,n)
@inbounds for i in 1:n
k[i] = read(io, Char)
end
return join(k)
end

read_str(io::IOStream) = read_str(io, read(io, Int64))

const LIT_ENDIAN = 0x04030201
const BIG_ENDIAN = 0x01020304

const LIT_ENDFLAG = UInt8(0)
const BIG_ENDFLAG = UInt8(255)

function write_endian(io::IOStream)
if Base.ENDIAN_BOM == LIT_ENDIAN
write(io, LIT_ENDFLAG)
elseif Base.ENDIAN_BOM == BIG_ENDIAN
write(io, BIG_ENDFLAG)
else
error("ENDIAN_BOM of ", Base.ENDIAN_BOM, " not recognized")
end
end

function read_endian(io::IOStream)
endflag = read(io, UInt8)
if endflag == LIT_ENDFLAG
endian = LIT_ENDIAN
elseif endflag == BIG_ENDFLAG
endian = BIG_ENDIAN
else
error("ENDIAN FLAG of ", endflag, " not recognized")
end
endian == Base.ENDIAN_BOM || error("endian does not match machine endian")
return endian
end
#---------------------------------------------------------------------------------------------------

## from https://github.com/JuliaLang/julia/blob/master/base/bitarray.jl (2020/01/20)
# notes: bits are stored in contiguous chunks
Expand All @@ -102,9 +81,10 @@ function _sizeof(::Type{BitArray{N}}, sz::NTuple{N,Int64}) where N
end

_sizeof(::Type{A}, sz::NTuple{N,Int64}) where {T,N,A<:AbstractArray{T,N}} = sizeof(T)*prod(sz)
_sizeof(::Type{String}, sz::Tuple{Int64}) = sizeof(Char)*first(sz)
_sizeof(::Type{String}, n::Int64) = sizeof(Char)*n
_sizeof(s::String) = length(s)*sizeof(Char)

#_sizeof(x::AbstractArray) = _sizeof(typeof(x), size(x))
#---------------------------------------------------------------------------------------------------

struct AbfKey{N}
pos::Int64
Expand All @@ -113,17 +93,21 @@ struct AbfKey{N}

# used when writing
AbfKey(pos::Int64, x::A) where A<:AbstractArray = new{ndims(A)}(pos, A, size(x))
AbfKey(io::IOStream, x::AbstractString) = new{1}(position(io), String, (1,))

# used when reading
AbfKey(io::IOStream, ::Type{A}, dims::NTuple{N,Int64}) where {A,N} = new{N}(position(io), A, dims)

# used when writing
AbfKey(io::IOStream, x::AbstractString) = new{1}(position(io), String, (length(x),))
AbfKey(io::IOStream, ::Type{A}, dims::NTuple{N,Int64}
) where {T,N,A<:AbstractArray{T,N}} = new{N}(position(io), A, dims)
AbfKey(io::IOStream, ::Type{String}) = new{1}(position(io), String, (1,))
end

Base.show(io::IO, a::AbfKey) = print(io, a.T, a.dims)

include("read_write.jl")
include("abffile.jl")
#---------------------------------------------------------------------------------------------------

include("./endian.jl")
include("./read_write.jl")
include("./abffile.jl")
include("./showio.jl")

end
61 changes: 8 additions & 53 deletions src/abffile.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
mutable struct AbfFile
io::IOStream
rw::String
abfkeys::Base.ImmutableDict{String,AbfKey}
loaded::Base.ImmutableDict{String,Union{Array,BitArray,String}}

function AbfFile(filename::String, rw::String)
io = open(filename, rw)
function AbfFile(io::IOStream)
abfkeys=Base.ImmutableDict{String,AbfKey}()
loaded=Base.ImmutableDict{String,Union{Array,BitArray,String}}()
new(io, rw, abfkeys, loaded)
new(io, abfkeys, loaded)
end

AbfFile(filename::String, rw::String) = AbfFile(open(filename, rw))
end

Base.keys(abf::AbfFile) = keys(abf.abfkeys)
Expand All @@ -32,7 +32,6 @@ function abfopen(filename::String, rw::String)
end

function Base.close(abf::AbfFile)
abf.rw = "closed"
abf.abfkeys = Base.ImmutableDict{String,AbfKey}()
abf.loaded = Base.ImmutableDict{String,Union{Array,BitArray,String}}()
close(abf.io)
Expand All @@ -49,19 +48,20 @@ function abfopen(f::Function, args...)
end

function Base.write(abf::AbfFile, k::String, x)
check_writable(abf.io)
seekend(abf.io)
abfkey = _write(abf.io::IOStream, k, x)
addabfkey!(abf, k, abfkey)
nothing
end

function Base.read(abf::AbfFile, k::String)
isreadable(abf.io) || error("file is not readable, opened with: ", abf.rw)
check_readable(abf.io)
k keys(abf.loaded) && return abf.loaded[k]
abfkey = abf.abfkeys[k]
seek(abf.io, abfkey.pos)
if abfkey.T == String
x = read_str(abf.io, first(abfkey.dims))
x = read_str(abf.io)
else
x = Mmap.mmap(abf.io, abfkey.T, abfkey.dims)
end
Expand All @@ -73,53 +73,8 @@ Base.getindex(abf::AbfFile, k::String) = read(abf, k)

function Base.setindex!(abf::AbfFile, v, k::String)
if k keys(abf)
error("cannot overwrite exists key. You may have wanted to do \"abf[", k,
error("cannot overwrite existing key. You may have wanted to do \"abf[", k,
"] .= x\" instead (element assignment)")
end
write(abf, k, v)
end

#---------------------------------------------------------------------------------------------------

function cpad(str, n::Int)
nspace = (n - length(str))/2
repeat(" ", floor(Int, nspace))*str*repeat(" ", ceil(Int, nspace))
end

function Base.show(io::IO, abf::T) where T<:AbfFile
println(io, T, "(", abf.rw, " ", abf.io.name, ")")

ktitle = "label"
ttitle = "type"
ltitle = "status"
keypad = length(ktitle)
typepad = length(ttitle)
loadpad = length(ltitle)

for (k,t) in abf.abfkeys
keypad = max(keypad, length(string(k)))
typepad = max(typepad, length(string(t)))
if k in keys(abf.loaded)
loadpad = max(loadpad, length("loaded"))
else
loadpad = max(loadpad, length("not loaded"))
end
end

indent = ""

println(io, indent, "┌─", repeat('', keypad) , "─┬─", repeat('', typepad), "─┬─", repeat('', loadpad), "─┐")
println(io, indent, "", cpad(ktitle, keypad), "", cpad(ttitle, typepad), "", cpad(ltitle, loadpad), "")
println(io, indent, "├─", repeat('', keypad) , "─┼─", repeat('', typepad), "─┼─", repeat('', loadpad), "─┤")

for (k,t) in sort(collect(abf.abfkeys), by=first)
print(io,indent, "", cpad(k, keypad), "", cpad(string(t), typepad), "")
if k in keys(abf.loaded)
println(io, cpad("loaded", loadpad), "")
else
println(io, cpad("not loaded", loadpad), "")
end
end
print(io, indent, "└─", repeat('', keypad) , "─┴─", repeat('', typepad), "─┴─", repeat('', loadpad), "─┘")
end

28 changes: 28 additions & 0 deletions src/endian.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const LIT_ENDIAN = 0x04030201
const BIG_ENDIAN = 0x01020304

const LIT_ENDFLAG = UInt8(0)
const BIG_ENDFLAG = UInt8(255)

function write_endian(io::IOStream)
if Base.ENDIAN_BOM == LIT_ENDIAN
write(io, LIT_ENDFLAG)
elseif Base.ENDIAN_BOM == BIG_ENDIAN
write(io, BIG_ENDFLAG)
else
error("ENDIAN_BOM of ", Base.ENDIAN_BOM, " not recognized")
end
end

function read_endian(io::IOStream)
endflag = read(io, UInt8)
if endflag == LIT_ENDFLAG
endian = LIT_ENDIAN
elseif endflag == BIG_ENDFLAG
endian = BIG_ENDIAN
else
error("ENDIAN FLAG of ", UInt8(endflag), " not recognized")
end
endian == Base.ENDIAN_BOM || error("endian does not match machine endian")
return endian
end
Loading

0 comments on commit 6f134a4

Please sign in to comment.