Right, I also saw a “wider hitting”. In my serializer code below, I do not yet use it.
The task to write as much as possible in one go is limited here to the next (element) level.
A struct is deserialized into named tuples, are there ways to get it back to its original type?
(de-)serializer
"""
io = serialize(v) reinterprets a Julia object into a series of bytes.
v = deserialize(io) recreates the data from a byte stream
An exercise in dispatch style.
Based on
https://de.mathworks.com/matlabcentral/fileexchange/29457-serialize-deserialize
and "julianized" with the experts on
https://discourse.julialang.org/
"""
# Type encoding
tcode = [
0 Float64;
1 Float32;
2 Float16;
3 Bool;
4 Int8;
5 UInt8;
6 Int16;
7 UInt16;
8 Int32;
9 UInt32;
10 Int64;
11 UInt64
12 Char;
13 String;
100 Tuple;
200 Any
]
STRUCT = 255
WRITABLE = 0:12
tcode2type = Dict(tcode[:,1] .=> tcode[:,2])
type2tcode = Dict(tcode[:,2] .=> tcode[:,1])
function serialize(io, v)
te = eltype(v)
if typeof(v) <: Tuple
write(io, UInt8(type2tcode[Tuple]))
write(io, UInt8(1))
write(io, UInt32(length(v)))
serialize.(Ref(io), v)
elseif typeof(v) == String
write(io, UInt8(type2tcode[String]))
write(io, UInt8(1))
write(io, UInt32(length(v)))
write(io, v)
elseif eltype(v) <: Real || eltype(v) == Char
write(io, UInt8(type2tcode[te]))
nd = ndims(v)
write(io, UInt8(nd))
if nd > 0
write(io, UInt32.(size(v))...)
end
write(io, v)
elseif v isa AbstractArray
if te == Any || te <: Tuple || te == String
write(io, UInt8(type2tcode[Any]));
write(io, UInt8(ndims(v)))
write(io, UInt32.(size(v))...)
serialize.(Ref(io), v)
elseif applicable(fieldcount, te) && fieldcount(te) > 0
println(v)
fc = fieldcount(eltype(v))
write(io, UInt32(fc))
for name in fieldnames(typeof(first(v)))
sname = String(name)
len = ncodeunits(sname)
write(io, UInt8(len))
writestr(io, sname)
serialize(io, getfield.(v, name))
end
else
error("no match for te=$te")
end
elseif applicable(fieldcount, te) && fieldcount(te) > 0
write(io, UInt8(STRUCT))
write(io, UInt8(0))
fc = fieldcount(typeof(v))
write(io, UInt32(fc))
for name in fieldnames(typeof(v))
sname = String(name)
len = ncodeunits(sname)
writen(io, UInt8(len))
writestr(io, sname)
serialize(io, getfield(v, name))
end
else
error("expected struct, but did not find any field")
end
end
function deserialize(io)
ity = Int(readnum(io, UInt8))
ndms = Int(readnum(io, UInt8))
dms = ndms == 0 ? 1 : Int.(readnum(io, UInt32, ndms))
if ity in WRITABLE
cls = tcode2type[ity]
return ndms == 0 ? readnum(io, cls) : reshape(readnum(io, cls, prod(dms)), dms...)
elseif ity == STRUCT
fname = Symbol[]
nfld = readnum(io, UInt32)
fdata = []
for i = 1:nfld
fn = readstr(io, readnum(io, UInt8))
push!(fname, Symbol(fn))
push!(fdata, deserialize(io))
end
if ndms == 0
return NamedTuple(zip.(Ref(fname), zip(fdata...)))
else
return reshape(NamedTuple.(zip.(Ref(fname), zip(fdata...))), dms...)
end
elseif ity == type2tcode[String]
if ndms == 1
return String(read(io, dms[1]))
else
se = String[]
for i = 1:prod(dms)
push!(se, deserialize(io))
end
return reshape(se, dms...)
end
elseif ity == type2tcode[Any] || ity == type2tcode[Tuple]
istuple = ity == type2tcode[Tuple]
ele = []
for i = 1:prod(dms)
push!(ele, deserialize(io))
end
if istuple
return Tuple(ele)
else
return reshape(ele, dms...)
end
else
error("unknown type index $ity")
end
end
############## IO read/write routines ##############
# write number as type T
function writenum(io, num, T)
write(io, T(num))
end
# read n numbers of type T
function readnum(io, T, n)
s = sizeof(T)
f = zeros(UInt8, s*n)
readbytes!(io, f, s*n)
return reinterpret(T, f)
end
# read single number of type T
function readnum(io, T)
s = sizeof(T)
f = zeros(UInt8, s)
readbytes!(io, f, s)
return reinterpret(T, f)[1]
end
# write string
function writestr(io, str)
write(io, codeunits(str))
end
# read string of length n
function readstr(io, n)
return String(read(io, n))
end
############## Tests ##############
function round_trip(data)
println(data)
open("io.bin", "w") do io
serialize(io, data)
end
println("..deserialize..")
io = open("io.bin", "r")
data2 = deserialize(io)
close(io)
data2
end
mutable struct Coords
x::Float64
y::Float64
z::Float64
end
Coords() = Coords(rand(), rand(), rand())
Array_of_Int = [1, 2]
Array_of_Tuple = [(1, 2), (2, 3)]
Array_of_Any = ["Ab", (1, 2)]
Single_Num = pi
Array_of_Num = randn(3,3)
Single_Struct = Coords()
Array_of_Struct = [Coords() for i in 1:5]
Single_Tuple = ("Ab", [pi, 2.0])
Single_String = "toto"
Array_of_String = ["Ab" "toto"; "titi" "ok"]
Array_of_Char = ['a' 'b'; 'c' 'd']
round_trip(Array_of_Any)