From 5bf72d1fa60e195641cb9347c5e5bd98e85da72e Mon Sep 17 00:00:00 2001 From: nobu Date: Tue, 7 Dec 2010 13:05:26 +0000 Subject: * transcode.c (transcode_loop): call default handler of the given hash, method, proc or [] method as fallback. [ruby-dev:42692] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30118 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 +++++ include/ruby/intern.h | 1 + proc.c | 10 ++++++--- test/ruby/test_transcode.rb | 17 ++++++++++++++++ transcode.c | 49 +++++++++++++++++++++++++++++++++++++++------ 5 files changed, 73 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3fc6863435..c7f066f6e2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Tue Dec 7 22:05:25 2010 Nobuyoshi Nakada + + * transcode.c (transcode_loop): call default handler of the given + hash, method, proc or [] method as fallback. [ruby-dev:42692] + Tue Dec 7 21:59:37 2010 Kouhei Sutou * lib/rexml/light/node.rb: remove circular require. diff --git a/include/ruby/intern.h b/include/ruby/intern.h index d1224334f7..46fcb03c58 100644 --- a/include/ruby/intern.h +++ b/include/ruby/intern.h @@ -340,6 +340,7 @@ int rb_proc_arity(VALUE); VALUE rb_proc_lambda_p(VALUE); VALUE rb_binding_new(void); VALUE rb_obj_method(VALUE, VALUE); +VALUE rb_obj_is_method(VALUE); VALUE rb_method_call(int, VALUE*, VALUE); int rb_mod_method_arity(VALUE, ID); int rb_obj_method_arity(VALUE, ID); diff --git a/proc.c b/proc.c index 0489dd967f..9ecf62656e 100644 --- a/proc.c +++ b/proc.c @@ -28,7 +28,6 @@ VALUE rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc); static VALUE bmcall(VALUE, VALUE); static int method_arity(VALUE); -static int rb_obj_is_method(VALUE m); rb_iseq_t *rb_method_get_iseq(VALUE method); /* Proc */ @@ -893,10 +892,15 @@ static const rb_data_type_t method_data_type = { }, }; -static inline int +VALUE rb_obj_is_method(VALUE m) { - return rb_typeddata_is_kind_of(m, &method_data_type); + if (rb_typeddata_is_kind_of(m, &method_data_type)) { + return Qtrue; + } + else { + return Qfalse; + } } static VALUE diff --git a/test/ruby/test_transcode.rb b/test/ruby/test_transcode.rb index 8aaaee5043..267ecf90d9 100644 --- a/test/ruby/test_transcode.rb +++ b/test/ruby/test_transcode.rb @@ -1947,4 +1947,21 @@ class TestTranscode < Test::Unit::TestCase assert_equal("[ISU]", "\u{1F4BA}".encode("SJIS-KDDI", fallback: {"\u{1F4BA}" => "[ISU]"})) end + + def test_fallback_hash_default + fallback = Hash.new {|h, x| "U+%.4X" % x.unpack("U")} + assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback)) + end + + def test_fallback_proc + fallback = proc {|x| "U+%.4X" % x.unpack("U")} + assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback)) + end + + def test_fallback_method + def (fallback = "U+%.4X").escape(x) + self % x.unpack("U") + end + assert_equal("U+3042", "\u{3042}".encode("US-ASCII", fallback: fallback.method(:escape))) + end end diff --git a/transcode.c b/transcode.c index cd4829f291..96f8e4be16 100644 --- a/transcode.c +++ b/transcode.c @@ -21,7 +21,7 @@ VALUE rb_eConverterNotFoundError; VALUE rb_cEncodingConverter; -static VALUE sym_invalid, sym_undef, sym_replace, sym_fallback; +static VALUE sym_invalid, sym_undef, sym_replace, sym_fallback, sym_aref; static VALUE sym_xml, sym_text, sym_attr; static VALUE sym_universal_newline; static VALUE sym_crlf_newline; @@ -2240,6 +2240,26 @@ output_replacement_character(rb_econv_t *ec) } #if 1 +#define hash_fallback rb_hash_aref + +static VALUE +proc_fallback(VALUE fallback, VALUE c) +{ + return rb_proc_call(fallback, rb_ary_new4(1, &c)); +} + +static VALUE +method_fallback(VALUE fallback, VALUE c) +{ + return rb_method_call(1, &c, fallback); +} + +static VALUE +aref_fallback(VALUE fallback, VALUE c) +{ + return rb_funcall3(fallback, sym_aref, 1, &c); +} + static void transcode_loop(const unsigned char **in_pos, unsigned char **out_pos, const unsigned char *in_stop, unsigned char *out_stop, @@ -2257,13 +2277,27 @@ transcode_loop(const unsigned char **in_pos, unsigned char **out_pos, int max_output; VALUE exc; VALUE fallback = Qnil; + VALUE (*fallback_func)(VALUE, VALUE) = 0; ec = rb_econv_open_opts(src_encoding, dst_encoding, ecflags, ecopts); if (!ec) rb_exc_raise(rb_econv_open_exc(src_encoding, dst_encoding, ecflags)); - if (!NIL_P(ecopts) && TYPE(ecopts) == T_HASH) + if (!NIL_P(ecopts) && TYPE(ecopts) == T_HASH) { fallback = rb_hash_aref(ecopts, sym_fallback); + if (RB_TYPE_P(fallback, T_HASH)) { + fallback_func = hash_fallback; + } + else if (rb_obj_is_proc(fallback)) { + fallback_func = proc_fallback; + } + else if (rb_obj_is_method(fallback)) { + fallback_func = method_fallback; + } + else { + fallback_func = aref_fallback; + } + } last_tc = ec->last_tc; max_output = last_tc ? last_tc->transcoder->max_output : 1; @@ -2275,8 +2309,8 @@ transcode_loop(const unsigned char **in_pos, unsigned char **out_pos, (const char *)ec->last_error.error_bytes_start, ec->last_error.error_bytes_len, rb_enc_find(ec->last_error.source_encoding)); - rep = rb_hash_lookup2(fallback, rep, Qundef); - if (rep != Qundef) { + rep = (*fallback_func)(fallback, rep); + if (rep != Qundef && !NIL_P(rep)) { StringValue(rep); ret = rb_econv_insert_output(ec, (const unsigned char *)RSTRING_PTR(rep), RSTRING_LEN(rep), rb_enc_name(rb_enc_get(rep))); @@ -2479,8 +2513,10 @@ rb_econv_prepare_opts(VALUE opthash, VALUE *opts) v = rb_hash_aref(opthash, sym_fallback); if (!NIL_P(v)) { - v = rb_convert_type(v, T_HASH, "Hash", "to_hash"); - if (!NIL_P(v)) { + VALUE h = rb_check_hash_type(v); + if (NIL_P(h) + ? (rb_obj_is_proc(v) || rb_obj_is_method(v) || rb_respond_to(v, sym_aref)) + : (v = h, 1)) { if (NIL_P(newhash)) newhash = rb_hash_new(); rb_hash_aset(newhash, sym_fallback, v); @@ -4258,6 +4294,7 @@ Init_transcode(void) sym_undef = ID2SYM(rb_intern("undef")); sym_replace = ID2SYM(rb_intern("replace")); sym_fallback = ID2SYM(rb_intern("fallback")); + sym_aref = ID2SYM(rb_intern("[]")); sym_xml = ID2SYM(rb_intern("xml")); sym_text = ID2SYM(rb_intern("text")); sym_attr = ID2SYM(rb_intern("attr")); -- cgit v1.2.3