diff options
Diffstat (limited to 'ext/marshal/marshal.c')
-rw-r--r-- | ext/marshal/marshal.c | 519 |
1 files changed, 356 insertions, 163 deletions
diff --git a/ext/marshal/marshal.c b/ext/marshal/marshal.c index 0b29ad5ab8..eae8d7fe09 100644 --- a/ext/marshal/marshal.c +++ b/ext/marshal/marshal.c @@ -13,24 +13,52 @@ #include "io.h" #include "st.h" +#define MARSHAL_MAJOR 2 +#define MARSHAL_MINOR 1 + #define TYPE_NIL '0' +#define TYPE_TRUE 'T' +#define TYPE_FALSE 'F' #define TYPE_FIXNUM 'i' #define TYPE_OBJECT 'o' -#define TYPE_LINK '@' +#define TYPE_USERDEF 'u' #define TYPE_FLOAT 'f' #define TYPE_BIGNUM 'l' #define TYPE_STRING '"' +#define TYPE_STRING2 '\'' #define TYPE_REGEXP '/' #define TYPE_ARRAY '[' +#define TYPE_ARRAY2 ']' #define TYPE_HASH '{' +#define TYPE_HASH2 '}' #define TYPE_STRUCT 'S' +#define TYPE_SYMBOL ':' +#define TYPE_SYMLINK ';' + +VALUE cString; +VALUE cArray; +VALUE cHash; + char *rb_class2path(); VALUE rb_path2class(); static ID s_dump, s_load; +#if (defined(linux) && defined(USE_DLN_A_OUT)) || !defined(HAVE_TMPNAM) +#define tmpnam(s) ltmpnam(s) +static char * +tmpnam(s) + char *s; +{ + static int n = 0; + + sprintf(s, "/tmp/rb-mrsr-%x%x", getpid(), n++); + return s; +} +#endif + #define w_byte(c, fp) putc((c), fp) #define w_bytes(s, n, fp) (w_long((n), fp),fwrite(s, 1, n, fp)) @@ -66,70 +94,119 @@ w_float(d, fp) } static void -w_symbol(id, fp) +w_symbol(id, fp, table) ID id; FILE *fp; + st_table *table; { char *sym = rb_id2name(id); + int num; - w_bytes(sym, strlen(sym), fp); + if (st_lookup(table, id, &num)) { + w_byte(TYPE_SYMLINK, fp); + w_long(num, fp); + } + else { + w_byte(TYPE_SYMBOL, fp); + w_bytes(sym, strlen(sym), fp); + st_insert(table, id, table->num_entries); + } +} + +static void +w_unique(s, fp, table) + char *s; + FILE *fp; + st_table *table; +{ + w_symbol(rb_intern(s), fp, table); } static void w_object(); extern VALUE cBignum, cStruct; +struct each_arg { + FILE *fp; + VALUE limit; + st_table *table; +}; + static int -hash_each(key, value, fp) +hash_each(key, value, arg) VALUE key, value; - FILE *fp; + struct each_arg *arg; { - w_object(key, fp); - w_object(value, fp); + w_object(key, arg->fp, arg->limit, arg->table); + w_object(value, arg->fp, arg->limit, arg->table); return ST_CONTINUE; } static int -obj_each(id, value, fp) +obj_each(id, value, arg) ID id; VALUE value; - FILE *fp; + struct each_arg *arg; { - w_symbol(id, fp); - w_object(value, fp); + w_symbol(id, arg->fp, arg->table); + w_object(value, arg->fp, arg->limit, arg->table); return ST_CONTINUE; } -struct st_table *new_idhash(); - static void -w_object(obj, fp, port, table) - VALUE obj, port; +w_object(obj, fp, limit, table) + VALUE obj; FILE *fp; + int limit; st_table *table; { + struct each_arg arg; + int n; + + if (limit == 0) { + Fail("exceed depth limit"); + } + limit--; + + arg.fp = fp; + arg.limit = limit; + arg.table = table; + if (obj == Qnil) { w_byte(TYPE_NIL, fp); } - else if (FIXNUM_P(obj)) { - w_byte(TYPE_FIXNUM, fp); - w_long(FIX2INT(obj), fp); + else if (obj == TRUE) { + w_byte(TYPE_TRUE, fp); + } + else if (obj == FALSE) { + w_byte(TYPE_FALSE, fp); } - else if (st_lookup(table, obj, 0)) { - w_byte(TYPE_LINK, fp); - w_long(obj, fp); + else if (FIXNUM_P(obj)) { + if (sizeof(long) == 4) { + w_byte(TYPE_FIXNUM, fp); + w_long(FIX2INT(obj), fp); + } } else { - st_insert(table, obj, 0); + if (rb_respond_to(obj, s_dump)) { + VALUE v; + + w_byte(TYPE_USERDEF, fp); + w_unique(rb_class2path(CLASS_OF(obj)), fp, table); + v = rb_funcall(obj, s_dump, 1, limit); + if (TYPE(v) != T_STRING) { + TypeError("_dump_to must return String"); + } + w_bytes(RSTRING(v)->ptr, RSTRING(v)->len, fp); + return; + } switch (BUILTIN_TYPE(obj)) { case T_FLOAT: w_byte(TYPE_FLOAT, fp); - w_long(obj, fp); w_float(RFLOAT(obj)->value, fp); - break; + return; case T_BIGNUM: w_byte(TYPE_BIGNUM, fp); - w_long(obj, fp); { char sign = RBIGNUM(obj)->sign?'+':'-'; int len = RBIGNUM(obj)->len; @@ -142,80 +219,92 @@ w_object(obj, fp, port, table) d++; } } - break; + return; + } + switch (BUILTIN_TYPE(obj)) { case T_STRING: - w_byte(TYPE_STRING, fp); - w_long(obj, fp); - w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, fp); - break; + if (CLASS_OF(obj) == cString) { + w_byte(TYPE_STRING, fp); + w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, fp); + } + else { + w_byte(TYPE_STRING2, fp); + w_bytes(RSTRING(obj)->ptr, RSTRING(obj)->len, fp); + w_unique(rb_class2path(CLASS_OF(obj)), fp, table); + } + return; case T_REGEXP: w_byte(TYPE_REGEXP, fp); - w_long(obj, fp); w_bytes(RREGEXP(obj)->str, RREGEXP(obj)->len, fp); w_byte(FL_TEST(obj, FL_USER1), fp); - break; + return; case T_ARRAY: - w_byte(TYPE_ARRAY, fp); - w_long(obj, fp); + if (CLASS_OF(obj) == cArray) w_byte(TYPE_ARRAY, fp); + else w_byte(TYPE_ARRAY2, fp); { int len = RARRAY(obj)->len; VALUE *ptr = RARRAY(obj)->ptr; w_long(len, fp); while (len--) { - w_object(*ptr, fp, port, table); + w_object(*ptr, fp, limit, table); ptr++; } } + if (CLASS_OF(obj) != cArray) { + w_unique(rb_class2path(CLASS_OF(obj)), fp, table); + } break; case T_HASH: + if (CLASS_OF(obj) == cHash) w_byte(TYPE_HASH, fp); + else w_byte(TYPE_HASH2, fp); w_byte(TYPE_HASH, fp); - w_long(obj, fp); w_long(RHASH(obj)->tbl->num_entries, fp); - st_foreach(RHASH(obj)->tbl, hash_each, fp); + st_foreach(RHASH(obj)->tbl, hash_each, &arg); + if (CLASS_OF(obj) != cHash) { + w_unique(rb_class2path(CLASS_OF(obj)), fp, table); + } break; case T_STRUCT: w_byte(TYPE_STRUCT, fp); - w_long(obj, fp); { int len = RSTRUCT(obj)->len; char *path = rb_class2path(CLASS_OF(obj)); VALUE mem; int i; - w_bytes(path, strlen(path), fp); + w_unique(path, fp, table); w_long(len, fp); mem = rb_ivar_get(CLASS_OF(obj), rb_intern("__member__")); if (mem == Qnil) { - Fail("non-initialized struct"); + Fatal("non-initialized struct"); } for (i=0; i<len; i++) { - w_symbol(FIX2INT(RARRAY(mem)->ptr[i]), fp); - w_object(RSTRUCT(obj)->ptr[i], fp, port, table); + w_symbol(FIX2INT(RARRAY(mem)->ptr[i]), fp, table); + w_object(RSTRUCT(obj)->ptr[i], fp, limit, table); } } break; case T_OBJECT: w_byte(TYPE_OBJECT, fp); - w_long(obj, fp); { VALUE class = CLASS_OF(obj); - char *path = rb_class2path(class); + char *path; - w_bytes(path, strlen(path), fp); - if (rb_responds_to(obj, s_dump)) { - w_long(-1, fp); - rb_funcall(obj, s_dump, 1, port); + if (FL_TEST(class, FL_SINGLETON)) { + TypeError("singleton can't be dumped"); } - else if (ROBJECT(obj)->iv_tbl) { + path = rb_class2path(class); + w_unique(path, fp, table); + if (ROBJECT(obj)->iv_tbl) { w_long(ROBJECT(obj)->iv_tbl->num_entries, fp); - st_foreach(ROBJECT(obj)->iv_tbl, obj_each, fp); + st_foreach(ROBJECT(obj)->iv_tbl, obj_each, &arg); } else { w_long(0, fp); @@ -224,59 +313,108 @@ w_object(obj, fp, port, table) break; default: - Fail("can't dump %s", rb_class2name(CLASS_OF(obj))); + TypeError("can't dump %s", rb_class2name(CLASS_OF(obj))); break; } } } +struct dump_arg { + VALUE obj; + FILE *fp; + int limit; + st_table *table; +}; + static VALUE -marshal_dump(self, obj, port) - VALUE self, obj, port; +dump(arg) + struct dump_arg *arg; +{ + w_object(arg->obj, arg->fp, arg->limit, arg->table); +} + +static VALUE +dump_ensure(arg) + struct dump_arg *arg; +{ + st_free_table(arg->table); +} + +static VALUE +dump_on(obj, port, limit) + VALUE obj, port; + int limit; { extern VALUE cIO; FILE *fp; OpenFile *fptr; - st_table *table; + struct dump_arg arg; if (obj_is_kind_of(port, cIO)) { GetOpenFile(port, fptr); - if (!(fptr->mode & FMODE_WRITABLE)) { - Fail("not opened for writing"); - } + io_writable(fptr); fp = (fptr->f2) ? fptr->f2 : fptr->f; } else { - Fail("instance of IO needed"); + TypeError("instance of IO needed"); } - table = new_idhash(); + w_byte(MARSHAL_MAJOR, fp); + w_byte(MARSHAL_MINOR, fp); - w_object(obj, fp, port, table); + arg.obj = obj; + arg.fp = fp; + arg.limit = limit; + arg.table = st_init_numtable(); + rb_ensure(dump, &arg, dump_ensure, &arg); - st_free_table(table); return Qnil; } static VALUE -marshal_dumps(self, obj) - VALUE self, obj; +marshal_dump(argc, argv) + int argc; + VALUE argv; +{ + VALUE obj, port, lim; + int limit; + + rb_scan_args(argc, argv, "21", &obj, &port, &lim); + if (NIL_P(lim)) limit = 100; + else limit = NUM2INT(lim); + + dump_on(obj, port, limit); +} + +static VALUE +marshal_dumps(argc, argv) + int argc; + VALUE argv; { + VALUE obj, lim; + int limit; VALUE str = str_new(0, 0); VALUE port; - FILE *fp = Qnil; + FILE *fp = 0; char buf[BUFSIZ]; int n; - sprintf(buf, "/tmp/rb-mrsr-%x", getpid()^(int)buf); + rb_scan_args(argc, argv, "11", &obj, &lim); + if (NIL_P(lim)) limit = 100; + else limit = NUM2INT(lim); + + tmpnam(buf); port = file_open(buf, "w"); - if (!port) rb_sys_fail("tmp file"); - fp = fopen(buf, "r"); - if (!fp) rb_sys_fail("tmp file(read)"); + fp = rb_fopen(buf, "r"); +#if !defined(MSDOS) && !defined(__BOW__) unlink(buf); +#endif - marshal_dump(self, obj, port); + dump_on(obj, port, limit); io_close(port); +#if defined(MSDOS) || defined(__BOW__) + unlink(buf); +#endif while (n = fread(buf, 1, BUFSIZ, fp)) { str_cat(str, buf, n); @@ -310,67 +448,96 @@ r_long(fp) /* XXX If your long is > 32 bits, add sign-extension here!!! */ return x; } -#define r_bytes(s, fp) r_bytes0(&s, fp) -static int -r_bytes0(s, fp) - char **s; + +#define r_bytes(s, fp) \ + (s = (char*)r_long(fp), r_bytes0(&s,ALLOCA_N(char,(int)s),(int)s,fp)) + +static char +r_bytes0(sp, s, len, fp) + char **sp, *s; + int len; FILE *fp; { - int len = r_long(fp); - *s = ALLOC_N(char, len+1); + fread(s, 1, len, fp); + (s)[len] = '\0'; + *sp = s; - fread(*s, 1, len, fp); - (*s)[len] = '\0'; return len; } static ID -r_symbol(fp) +r_symbol(fp, table) FILE *fp; + st_table *table; { char *buf; ID id; + char type; + if (r_byte(fp) == TYPE_SYMLINK) { + int num = r_long(fp); + + if (st_lookup(table, num, &id)) { + return id; + } + TypeError("bad symbol"); + } r_bytes(buf, fp); id = rb_intern(buf); - free(buf); + st_insert(table, table->num_entries, id); + return id; } +static char* +r_unique(fp, table) + FILE *fp; + st_table *table; +{ + return rb_id2name(r_symbol(fp, table)); +} + static VALUE -r_object(fp, port, table) +r_string(fp) + FILE *fp; +{ + char *buf; + int len = r_bytes(buf, fp); + VALUE v; + + v = str_new(buf, len); + + return v; +} + +static VALUE +r_object(fp, table) FILE *fp; - VALUE port; st_table *table; { VALUE v; int type = r_byte(fp); - int id; switch (type) { case EOF: - Fail("EOF read where object expected"); + eof_error("EOF read where object expected"); return Qnil; case TYPE_NIL: return Qnil; - case TYPE_LINK: - if (st_lookup(table, r_long(fp), &v)) { - return v; - } - Fail("corrupted marshal file"); - break; + case TYPE_TRUE: + return TRUE; + + case TYPE_FALSE: + return FALSE; case TYPE_FIXNUM: { int i = r_long(fp); return INT2FIX(i); } - } - id = r_long(fp); - switch (type) { case TYPE_FLOAT: { double atof(); @@ -378,9 +545,8 @@ r_object(fp, port, table) r_bytes(buf, fp); v = float_new(atof(buf)); - free(buf); + return v; } - break; case TYPE_BIGNUM: { @@ -395,18 +561,16 @@ r_object(fp, port, table) while (len--) { *digits++ = r_short(fp); } - v = (VALUE)big; + return (VALUE)big; } - break; case TYPE_STRING: - { - char *buf; - int len = r_bytes(buf, fp); - v = str_new(buf, len); - free(buf); - } - break; + return r_string(fp); + + case TYPE_STRING2: + v = r_string(fp); + RBASIC(v)->class = rb_path2class(r_unique(fp, table)); + return v; case TYPE_REGEXP: { @@ -414,19 +578,18 @@ r_object(fp, port, table) int len = r_bytes(buf, fp); int ci = r_byte(fp); v = reg_new(buf, len, ci); - free(buf); + return v; } - break; case TYPE_ARRAY: { int len = r_long(fp); v = ary_new2(len); while (len--) { - ary_push(v, r_object(fp, port, table)); + ary_push(v, r_object(fp, table)); } + return v; } - break; case TYPE_HASH: { @@ -434,120 +597,150 @@ r_object(fp, port, table) v = hash_new(); while (len--) { - VALUE key = r_object(fp, port, table); - VALUE value = r_object(fp, port, table); + VALUE key = r_object(fp, table); + VALUE value = r_object(fp, table); hash_aset(v, key, value); } + return v; } - break; case TYPE_STRUCT: { VALUE class, mem, values; - char *path; int i, len; - r_bytes(path, fp); - class = rb_path2class(path); - free(path); + class = rb_path2class(r_unique(fp, table)); mem = rb_ivar_get(class, rb_intern("__member__")); if (mem == Qnil) { - Fail("non-initialized struct"); + Fatal("non-initialized struct"); } len = r_long(fp); - values = ary_new(); + values = ary_new2(len); i = 0; - while (len--) { - ID slot = r_symbol(fp); - if (RARRAY(mem)->ptr[i++] != INT2FIX(slot)) - Fail("struct not compatible"); - ary_push(values, r_object(fp, port, table)); + for (i=0; i<len; i++) { + ID slot = r_symbol(fp, table); + if (RARRAY(mem)->ptr[i] != INT2FIX(slot)) + TypeError("struct not compatible"); + ary_push(values, r_object(fp, table)); } v = struct_alloc(class, values); } break; + case TYPE_USERDEF: + { + VALUE class; + int len; + + class = rb_path2class(r_unique(fp, table)); + if (rb_respond_to(class, s_load)) { + v = rb_funcall(class, s_load, 1, r_string(fp)); + } + else { + TypeError("class %s needs to have method `_load_from'", + rb_class2name(class)); + } + } + break; case TYPE_OBJECT: { VALUE class; int len; - char *path; - r_bytes(path, fp); - class = rb_path2class(path); - free(path); + class = rb_path2class(r_unique(fp, table)); len = r_long(fp); - if (len == -1) { - if (rb_responds_to(class, s_load)) { - v = rb_funcall(class, s_load, 1, port); - } - else { - Fail("class %s needs to have method `_load_from'", - rb_class2name(class)); - } - } - else { - v = obj_alloc(class); - if (len > 0) { - while (len--) { - ID id = r_symbol(fp); - VALUE val = r_object(fp, port, table); - rb_ivar_set(v, id, val); - } + v = obj_alloc(class); + if (len > 0) { + while (len--) { + ID id = r_symbol(fp, table); + VALUE val = r_object(fp, table); + rb_ivar_set(v, id, val); } } } break; default: - Fail("dump format error(0x%x)", type); + ArgError("dump format error(0x%x)", type); break; } - st_insert(table, id, v); return v; } +struct load_arg { + FILE *fp; + st_table *table; +}; + +static VALUE +load(arg) + struct load_arg *arg; +{ + return r_object(arg->fp, arg->table); +} + +static VALUE +load_ensure(arg) + struct load_arg *arg; +{ + st_free_table(arg->table); +} + static VALUE marshal_load(self, port) VALUE self, port; { extern VALUE cIO; - void *fp; + FILE *fp; + int major; VALUE v; OpenFile *fptr; - st_table *table; + char buf[32]; +#if defined(MSDOS) || defined(__BOW__) + int need_unlink_tmp = 0; +#endif + struct load_arg arg; if (TYPE(port) == T_STRING) { - char buf[32]; - - sprintf(buf, "/tmp/rb-mrsw-%x", getpid()^(int)buf); - fp = fopen(buf, "w"); - if (!fp) rb_sys_fail("tmp file"); + tmpnam(buf); + fp = rb_fopen(buf, "w"); v = file_open(buf, "r"); - if (!v) rb_sys_fail("tmp file(read)"); +#if defined(MSDOS) || defined(__BOW__) + need_unlink_tmp = 0; +#else unlink(buf); +#endif fwrite(RSTRING(port)->ptr, RSTRING(port)->len, 1, fp); fclose(fp); port = v; } + if (obj_is_kind_of(port, cIO)) { GetOpenFile(port, fptr); - if (!(fptr->mode & FMODE_READABLE)) { - Fail("not opened for reading"); - } + io_readable(fptr); fp = fptr->f; } else { - Fail("instance of IO needed"); + TypeError("instance of IO needed"); } - table = new_idhash(); - - v = r_object(fp, port, table); - - st_free_table(table); + major = r_byte(fp); + if (major == MARSHAL_MAJOR) { + if (r_byte(fp) != MARSHAL_MINOR) { + Warning("Old marshal file format (can be read)"); + } + arg.fp = fp; + arg.table = st_init_numtable(); + v = rb_ensure(load, &arg, load_ensure, &arg); + } +#if defined(MSDOS) || defined(__BOW__) + if (need_unlink_tmp) unlink(buf); +#endif + if (major != MARSHAL_MAJOR) { + TypeError("Old marshal file format (can't read)"); + } return v; } @@ -558,8 +751,8 @@ Init_marshal() s_dump = rb_intern("_dump_to"); s_load = rb_intern("_load_from"); - rb_define_module_function(mMarshal, "dump", marshal_dump, 2); - rb_define_module_function(mMarshal, "dumps", marshal_dumps, 1); + rb_define_module_function(mMarshal, "dump", marshal_dump, -1); + rb_define_module_function(mMarshal, "dumps", marshal_dumps, -1); rb_define_module_function(mMarshal, "load", marshal_load, 1); rb_define_module_function(mMarshal, "restore", marshal_load, 1); } |