Совместимость с HDF5
Библиотека JLD2 основана на спецификации формата HDF5 и создает файлы, совместимые с официальной библиотекой C HDF5.
Преимуществом является совместимость с другими библиотеками, использующими HDF5, такими как оболочка Julia HDF5.jl
или даже пакет h5py
для Python. Помимо этого, соблюдение стандартов HDF5 позволяет использовать инструменты интроспекции файлов, такие как h5dump
и h5debug
, предоставляемые группой HDF5.
В общем случае совместимость имеет место только для набора базовых типов: - числовых: |
Основные сведения о кодировании структур (struct
) Julia
Стандарт HDF5 поддерживает так называемые составные типы данных (compound datatypes
), которые состоят из набора известных типов данных. Они очень похожи на структуры (struct
) в Julia. Если пользователь хочет записать на диск данные нестандартного типа, JLD2 создает соответствующий составной тип и сохраняет его в файле. Все определения пользовательских типов в файле JLD2 хранятся в группе _types/
. Поэтому определения типов достаточно записать в файл один раз, после чего все экземпляры структуры (struct
) будут ссылаться на соответствующее определение.
Пример
julia> using JLD2
julia> struct MyCustomStruct
x::Int64
y::Float64
end
julia> @save "test.jld2" a=MyCustomStruct(42, π)
Давайте посмотрим, как JLD2 представит простой пример структуры MyCustomStruct
. Для этого мы изучим выходные данные h5dump
.
$> h5dump test.jld2
HDF5 "test.jld2" {
GROUP "/" {
GROUP "_types" {
DATATYPE "00000001" H5T_COMPOUND {
H5T_STRING {
STRSIZE H5T_VARIABLE;
STRPAD H5T_STR_NULLPAD;
CSET H5T_CSET_UTF8;
CTYPE H5T_C_S1;
} "name";
H5T_VLEN { H5T_REFERENCE { H5T_STD_REF_OBJECT }} "parameters";
}
ATTRIBUTE "julia_type" {
DATATYPE "/_types/00000001"
DATASPACE SCALAR
DATA {
(0): {
"Core.DataType",
()
}
}
}
DATATYPE "00000002" H5T_COMPOUND {
H5T_STD_I64LE "x";
H5T_IEEE_F64LE "y";
}
ATTRIBUTE "julia_type" {
DATATYPE "/_types/00000001"
DATASPACE SCALAR
DATA {
(0): {
"Main.MyCustomStruct",
()
}
}
}
}
DATASET "a" {
DATATYPE "/_types/00000002"
DATASPACE SCALAR
DATA {
(0): {
42,
3.14159
}
}
}
}
}
Как видите, на верхнем уровне файл содержит два компонента. Это набор данных "a"
(то, что мы хотели сохранить) и группа _types
, в которой помещается вся необходимая информация о типах.
Можно заметить, что библиотека JLD2 сохранила два составных типа данных. Первый — это Core.Datatype
, что на первый взгляд может показаться странным. Его назначение в том, чтобы сообщить HDF5, как выглядит сериализованный тип данных Julia (имя и список параметров).
Далее следует определение MyCustomStruct
с двумя полями: H5T_STD_I64LE "x"
и H5T_IEEE_F64LE "y"
, которые определяют целочисленное поле x
и поле с плавающей запятой y
.
Примечание по указателям
В языке программирования Julia необходимость в указателях (Ptr
) возникает нечасто. Однако если имеются двоичные зависимости и требуется постоянно передавать области памяти, указатели становятся важны. Указатели — это адреса участков в памяти, и после завершения работы программы они утрачивают свой смысл.
В принципе, хранить указатель на файл особого смысла нет, но для более последовательной работы JLD2, аналогично модулю Base.Serialization
, автоматически принимает указатели. Это полезно при сохранении больших структур, например объекта решения DifferentialEquations.jl
, в котором может быть указатель. При десериализации экземпляры полей указателей создаются как нулевые указатели.
Это делается с помощью всего трех строк кода, в которых используется пользовательская логика сериализации, и они приводятся здесь, так как являются хорошим примером применения данной функции.
writeas(::Type{<:Ptr}) = Nothing
rconvert(::Type{Ptr{T}}, ::Nothing) where {T} = Ptr{T}()
Как правило, обычно также необходимо определить метод для wconvert
. Однако в данном случае JLD2 определяет, что для создания nothing
явного преобразования не требуется.