Importing a binary file to a struct

Hi,
I’m trying to port over some C++ & IDL code to Julia, the code basically opens up a binary file which has a 512 byte header described by a c++ struct. The reaminder of the file is a stack of 8-bit greyscale images, the details of the stack are given in elements of the header.
I can create a struct in Julia to hold the header data, but trying to read in the file fails. This is the code I’m trying.

mutable struct tomheader
xsize::UInt16
ysize::UInt16
zsize::UInt16
lmarg::UInt16
rmarg::UInt16
tmarg::UInt16
bmarg::UInt16
tzmarg::UInt16
bzmarg::UInt16
num_samples::UInt16
num_proj::UInt16
num_blocks::UInt16
num_slices::UInt16
bin::UInt16
gain::UInt16
speed::UInt16
pepper::UInt16
calibrationissue::UInt16
num_frames::UInt16
machine::UInt16
spare_int::Array{UInt16,12}
scale::Float32
offset::Float32
voltage::Float32
current::Float32
thickness::Float32
pixel_size::Float32
distance::Float32
exposure::Float32
mag_factor::Float32
filterb::Float32
correction_factor::Float32
spare_float::Array{Float32,2}
z_shift::UInt32
z::UInt32
theta::UInt32
time::Array{UInt8,26}
duration::Array{UInt8,12}
owner::Array{UInt8,21}
user::Array{UInt8,5}
specimen::Array{UInt8,32}
scan::Array{UInt8,32}
comment::Array{UInt8,64}
spare_char::Array{UInt8,192}
end

f = open(“testfile.tom”,“r”)
th = Ref{tomheader}() // fails with Base.RefValue{tomheader}(undef)
read!(f, th,512)

Can anyone point me at what I’m doing wrong?

cheers,
David

Unless the C++ code is using Julia arrays you are using the wrong types, for example:
Array{UInt8,192}
This is a 192 dimensional Julia array. If the c code you are using specifies unsigned char[192] then the corresponding type is NTuple{192, Uint8} instead.

I don’t do much C interop, but I think you should be using a plain struct rather then a mutable one.

I am on my phone and don’t have a compiler to test with but assuming you made the above changes then

buffer = Vector{Uint8}(undef, 512)
readbytes!(f, buffer, 512)
th = reinterpret(tomheader, buffer)[1]

should do what you are trying to do.

edit: added undef

1 Like

Thank you for the pointer on not needing mutable struct and NTuple for an array of bytes.
It almost works!
I had to add a , to the buffer setup to make it

buffer = Vector{UInt8},(512)
It only produced an error otherwise.
Attempting the readbytes line gives the following:

MethodError: no method matching readbytes!(::IOStream, ::Tuple{DataType,Int64}, ::Int64)
Closest candidates are:
readbytes!(::IOStream, !Matched::Array{UInt8,N} where N, ::Any; all) at iostream.jl:484
readbytes!(::IO, !Matched::AbstractArray{UInt8,N} where N, ::Any) at io.jl:913
readbytes!(::IOStream, !Matched::Array{UInt8,N} where N) at iostream.jl:484

buffer = Vector{UInt8},(512) - That won’t really work. It’ll just make buffer a tuple of the Vector{UInt8} type as the first element, and the integer 512 as the second.
To initialize an empty array of length 512, use Vector{UInt8}(undef, 512).

Some code that loads your type from the first 512 bytes of a file may look like:

open("my_file.txt") do file
    buffer = Vector{UInt8}(undef, 512)
    readbytes!(file, buffer)
    return reinterpret(TomHeader, buffer)[1]
end
2 Likes