модуль Marshal
Библиотека маршализации преобразует коллекции объектов Ruby в поток байтов, позволяя сохранять их вне текущего активного скрипта. Эти данные могут быть позже прочитаны и из них могут быть восстановлены исходные объекты.
В маршализованных данных хранятся номера основной и дополнительной версии вместе с информацией об объекте. При обычном использовании маршализация может загрузить данные, записанные с тем же номером основной версии и равным или меньшим номером дополнительной версии. Если флаг «verbose» Ruby установлен (обычно с помощью -d, -v, -w или –verbose), номера основной и дополнительной версии должны точно совпадать. Версионирование Marshal независимо от номеров версий Ruby. Версию можно извлечь, прочитав первые два байта маршализованных данных.
str = Marshal.dump("thing")
RUBY_VERSION #=> "1.9.0"
str[0].ord #=> 4
str[1].ord #=> 8
Некоторые объекты не могут быть сохранены: если сохраняемые объекты включают привязки, объекты процедур или методов, экземпляры класса IO или одиночные объекты, будет возбуждено исключение TypeError.
Если ваш класс имеет особые потребности в сериализации (например, если вы хотите сериализовать в определенном формате) или содержит объекты, которые в противном случае не были бы сериализуемы, вы можете реализовать собственную стратегию сериализации.
Существует два способа сделать это: ваш объект может определить либо marshal_dump и marshal_load, либо _dump и _load. marshal_dump будет иметь приоритет над _dump, если оба определены. marshal_dump может привести к более коротким строкам Marshal.
Соображения безопасности
По своему дизайну, ::load может десериализовать практически любой класс, загруженный в процесс Ruby. Во многих случаях это может привести к выполнению удаленного кода, если данные Marshal загружены из ненадежного источника.
В результате ::load не подходит в качестве универсального формата сериализации, и вы никогда не должны расмаршализовывать введенные пользователем данные или другие ненадежные данные.
Если вам нужно десериализовать ненадежные данные, используйте JSON или другой формат сериализации, который может загружать только простые «примитивные» типы, такие как String, Array, Hash и т. д. Никогда не позволяйте вводимым пользователем данным указывать произвольные типы для десериализации.
marshal_dump и marshal_load
При сохранении объекта вызывается метод marshal_dump. marshal_dump должен возвращать результат, содержащий информацию, необходимую для marshal_load для восстановления объекта. Результатом может быть любой объект.
При загрузке объекта, сохраненного с помощью marshal_dump, объект сначала выделяется, а затем вызывается marshal_load с результатом от marshal_dump. marshal_load должен воссоздать объект из информации в результате.
Пример:
class MyObj
def initialize name, version, data
@name = name
@version = version
@data = data
end
def marshal_dump
[@name, @version]
end
def marshal_load array
@name, @version = array
end
end
_dump и _load
Используйте _dump и _load, когда вам нужно самостоятельно выделить объект, который вы восстанавливаете.
При сохранении объекта вызывается метод экземпляра _dump с целым числом, указывающим максимальную глубину объектов для сохранения (значение -1 означает, что вы должны отключить проверку глубины). _dump должен возвращать строку, содержащую информацию, необходимую для восстановления объекта.
Метод класса _load должен принимать строку и использовать ее для возврата объекта того же класса.
Пример:
class MyObj
def initialize name, version, data
@name = name
@version = version
@data = data
end
def _dump level
[@name, @version].join ':'
end
def self._load args
new(*args.split(':'))
end
end
Поскольку ::dump выводит строку, вы можете заставить _dump возвращать строку Marshal, которая загружается в _load для сложных объектов.
Константы
- MAJOR_VERSION
-
основная версия
- MINOR_VERSION
-
дополнительная версия
Общедоступные методы класса
static VALUE
marshal_dump(int argc, VALUE *argv)
{
VALUE obj, port, a1, a2;
int limit = -1;
struct dump_arg *arg;
VALUE wrapper; /* used to avoid memory leak in case of exception */
port = Qnil;
rb_scan_args(argc, argv, "12", &obj, &a1, &a2);
if (argc == 3) {
if (!NIL_P(a2)) limit = NUM2INT(a2);
if (NIL_P(a1)) io_needed();
port = a1;
}
else if (argc == 2) {
if (FIXNUM_P(a1)) limit = FIX2INT(a1);
else if (NIL_P(a1)) io_needed();
else port = a1;
}
wrapper = TypedData_Make_Struct(rb_cData, struct dump_arg, &dump_arg_data, arg);
arg->dest = 0;
arg->symbols = st_init_numtable();
arg->data = rb_init_identtable();
arg->infection = 0;
arg->compat_tbl = 0;
arg->encodings = 0;
arg->str = rb_str_buf_new(0);
if (!NIL_P(port)) {
if (!rb_respond_to(port, s_write)) {
io_needed();
}
arg->dest = port;
dump_check_funcall(arg, port, s_binmode, 0, 0);
}
else {
port = arg->str;
}
w_byte(MARSHAL_MAJOR, arg);
w_byte(MARSHAL_MINOR, arg);
w_object(obj, arg, limit);
if (arg->dest) {
rb_io_write(arg->dest, arg->str);
rb_str_resize(arg->str, 0);
}
clear_dump_arg(arg);
RB_GC_GUARD(wrapper);
return port;
} Сериализует объект obj и все дочерние объекты. Если anIO указан, сериализованные данные будут записаны в него, в противном случае данные будут возвращены как строка. Если limit указан, обход подчинённых объектов будет ограничен этой глубиной. Если limit отрицателен, проверка глубины не будет производиться.
class Klass
def initialize(str)
@str = str
end
def say_hello
@str
end
end
(не производит никакого вывода)
o = Klass.new("hello\n")
data = Marshal.dump(o)
obj = Marshal.load(data)
obj.say_hello #=> "hello\n"
Marshal не может сохранить следующие объекты:
-
анонимный класс/модуль.
-
объекты, связанные с системой (например: Dir, File::Stat, IO, File, Socket и так далее)
-
экземпляр MatchData, Data, Method, UnboundMethod, Proc, Thread, ThreadGroup, Continuation
-
объекты, которые определяют одиночные методы
static VALUE
marshal_load(int argc, VALUE *argv)
{
VALUE port, proc;
int major, minor, infection = 0;
VALUE v;
VALUE wrapper; /* used to avoid memory leak in case of exception */
struct load_arg *arg;
rb_scan_args(argc, argv, "11", &port, &proc);
v = rb_check_string_type(port);
if (!NIL_P(v)) {
infection = (int)FL_TEST(port, MARSHAL_INFECTION); /* original taintedness */
port = v;
}
else if (rb_respond_to(port, s_getbyte) && rb_respond_to(port, s_read)) {
rb_check_funcall(port, s_binmode, 0, 0);
infection = (int)FL_TAINT;
}
else {
io_needed();
}
wrapper = TypedData_Make_Struct(rb_cData, struct load_arg, &load_arg_data, arg);
arg->infection = infection;
arg->src = port;
arg->offset = 0;
arg->symbols = st_init_numtable();
arg->data = rb_init_identtable();
arg->compat_tbl = 0;
arg->proc = 0;
arg->readable = 0;
if (NIL_P(v))
arg->buf = xmalloc(BUFSIZ);
else
arg->buf = 0;
major = r_byte(arg);
minor = r_byte(arg);
if (major != MARSHAL_MAJOR || minor > MARSHAL_MINOR) {
clear_load_arg(arg);
rb_raise(rb_eTypeError, "incompatible marshal file format (can't be read)\n\
\tformat version %d.%d required; %d.%d given",
MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
}
if (RTEST(ruby_verbose) && minor != MARSHAL_MINOR) {
rb_warn("incompatible marshal file format (can be read)\n\
\tformat version %d.%d required; %d.%d given",
MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
}
if (!NIL_P(proc)) arg->proc = proc;
v = r_object(arg);
clear_load_arg(arg);
RB_GC_GUARD(wrapper);
return v;
} Возвращает результат преобразования сериализованных данных в source в объект Ruby (возможно, с связанными подчиненными объектами). source может быть либо экземпляром IO, либо объектом, который отвечает на to_str. Если proc указан, каждый объект будет передан в proc, по мере десериализации объекта.
Никогда не передавайте ненадежные данные (включая данные, введенные пользователем) в этот метод. Более подробную информацию см. в обзоре.
static VALUE
marshal_load(int argc, VALUE *argv)
{
VALUE port, proc;
int major, minor, infection = 0;
VALUE v;
VALUE wrapper; /* used to avoid memory leak in case of exception */
struct load_arg *arg;
rb_scan_args(argc, argv, "11", &port, &proc);
v = rb_check_string_type(port);
if (!NIL_P(v)) {
infection = (int)FL_TEST(port, MARSHAL_INFECTION); /* original taintedness */
port = v;
}
else if (rb_respond_to(port, s_getbyte) && rb_respond_to(port, s_read)) {
rb_check_funcall(port, s_binmode, 0, 0);
infection = (int)FL_TAINT;
}
else {
io_needed();
}
wrapper = TypedData_Make_Struct(rb_cData, struct load_arg, &load_arg_data, arg);
arg->infection = infection;
arg->src = port;
arg->offset = 0;
arg->symbols = st_init_numtable();
arg->data = rb_init_identtable();
arg->compat_tbl = 0;
arg->proc = 0;
arg->readable = 0;
if (NIL_P(v))
arg->buf = xmalloc(BUFSIZ);
else
arg->buf = 0;
major = r_byte(arg);
minor = r_byte(arg);
if (major != MARSHAL_MAJOR || minor > MARSHAL_MINOR) {
clear_load_arg(arg);
rb_raise(rb_eTypeError, "incompatible marshal file format (can't be read)\n\
\tformat version %d.%d required; %d.%d given",
MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
}
if (RTEST(ruby_verbose) && minor != MARSHAL_MINOR) {
rb_warn("incompatible marshal file format (can be read)\n\
\tformat version %d.%d required; %d.%d given",
MARSHAL_MAJOR, MARSHAL_MINOR, major, minor);
}
if (!NIL_P(proc)) arg->proc = proc;
v = r_object(arg);
clear_load_arg(arg);
RB_GC_GUARD(wrapper);
return v;
} Возвращает результат преобразования сериализованных данных в source в объект Ruby (возможно, с связанными подчиненными объектами). source может быть либо экземпляром IO, либо объектом, который отвечает на to_str. Если proc указан, каждый объект будет передан в proc, по мере десериализации объекта.
Никогда не передавайте ненадежные данные (включая данные, введенные пользователем) в этот метод. Более подробную информацию см. в обзоре.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.