diff --git a/mrbgems/mruby-encoding/mrbgem.rake b/mrbgems/mruby-encoding/mrbgem.rake new file mode 100644 index 000000000..8d5e41284 --- /dev/null +++ b/mrbgems/mruby-encoding/mrbgem.rake @@ -0,0 +1,8 @@ +MRuby::Gem::Specification.new('mruby-encoding') do |spec| + spec.license = 'MIT' + spec.author = 'mruby developers' + spec.summary = "Poorman's Encoding for mruby" + spec.build.defines << "HAVE_MRUBY_ENCODING_GEM" + spec.build.defines << "MRB_UTF8_STRING" + spec.add_test_dependency 'mruby-string-ext' +end diff --git a/mrbgems/mruby-encoding/src/encoding.c b/mrbgems/mruby-encoding/src/encoding.c new file mode 100644 index 000000000..cea66d4f0 --- /dev/null +++ b/mrbgems/mruby-encoding/src/encoding.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include + +#define ENC_ASCII_8BIT "ASCII-8BIT" +#define ENC_BINARY "BINARY" +#define ENC_UTF8 "UTF-8" + +#define ENC_COMP_P(enc, enc_lit) \ + casecmp_p(RSTRING_PTR(enc), RSTRING_LEN(enc), enc_lit, sizeof(enc_lit"")-1) + +static mrb_bool +casecmp_p(const char *s1, mrb_int len1, const char *s2, mrb_int len2) +{ + if (len1 != len2) return FALSE; + + const char *e1 = s1 + len1; + const char *e2 = s2 + len2; + while (s1 < e1 && s2 < e2) { + if (*s1 != *s2 && TOUPPER(*s1) != TOUPPER(*s2)) return FALSE; + s1++; + s2++; + } + return TRUE; +} + +/* + * call-seq: + * string.valid_encoding? -> true or false + * + * Returns true for a string which is encoded correctly. + * + */ +static mrb_value +str_valid_enc_p(mrb_state *mrb, mrb_value str) +{ +#define utf8_islead(c) ((unsigned char)((c)&0xc0) != 0x80) + + struct RString *s = mrb_str_ptr(str); + if (RSTR_SINGLE_BYTE_P(s)) return mrb_true_value(); + if (RSTR_BINARY_P(s)) return mrb_true_value(); + + mrb_int byte_len = RSTR_LEN(s); + mrb_int utf8_len = 0; + const char *p = RSTR_PTR(s); + const char *e = p + byte_len; + while (p < e) { + mrb_int len = mrb_utf8len(p, e); + + if (len == 1 && (*p & 0x80)) return mrb_false_value(); + p += len; + utf8_len++; + } + if (byte_len == utf8_len) RSTR_SET_SINGLE_BYTE_FLAG(s); + return mrb_true_value(); +} + +static mrb_value +str_b(mrb_state *mrb, mrb_value self) +{ + mrb_value str = mrb_str_dup(mrb, self); + mrb_str_ptr(str)->flags |= MRB_STR_BINARY; + return str; +} + +static mrb_value +get_encoding(mrb_state *mrb, mrb_sym enc) +{ + struct RClass *e = mrb_module_get_id(mrb, MRB_SYM(Encoding)); + return mrb_const_get(mrb, mrb_obj_value(e), enc); +} + +static mrb_value +str_encoding(mrb_state *mrb, mrb_value self) +{ + struct RString *s = mrb_str_ptr(self); + if (RSTR_BINARY_P(s)) { + return get_encoding(mrb, MRB_SYM(BINARY)); + } + return get_encoding(mrb, MRB_SYM(UTF_8)); +} + +static mrb_value +str_force_encoding(mrb_state *mrb, mrb_value self) +{ + mrb_value enc; + + mrb_get_args(mrb, "S", &enc); + + struct RString *s = mrb_str_ptr(self); + if (ENC_COMP_P(enc, ENC_ASCII_8BIT) || + ENC_COMP_P(enc, ENC_BINARY)) { + s->flags |= MRB_STR_BINARY; + } + else if (ENC_COMP_P(enc, ENC_UTF8)) { + s->flags &= ~MRB_STR_BINARY; + } + else { + mrb_raisef(mrb, E_ARGUMENT_ERROR, "unknown encoding name - %v", enc); + } + return self; +} + +void +mrb_mruby_encoding_gem_init(mrb_state* mrb) +{ + struct RClass *s = mrb->string_class; + + mrb_define_method_id(mrb, s, MRB_SYM(b), str_b, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, s, MRB_SYM_Q(valid_encoding), str_valid_enc_p, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, s, MRB_SYM(encoding), str_encoding, MRB_ARGS_NONE()); + mrb_define_method_id(mrb, s, MRB_SYM(force_encoding), str_force_encoding, MRB_ARGS_REQ(1)); + + /* Poorman's Encoding + * + * Encoding - module instead of class + * encodings - supports only UTF-8 and ASCII-8BIT (and its alias BINARY) + * each Encoding - encoding name string instead of Encoding object + * + */ + struct RClass *e = mrb_define_module_id(mrb, MRB_SYM(Encoding)); + mrb_value b = mrb_str_new_lit_frozen(mrb, ENC_ASCII_8BIT); + mrb_define_const_id(mrb, e, MRB_SYM(ASCII_8BIT), b); + mrb_define_const_id(mrb, e, MRB_SYM(BINARY), b); + mrb_define_const_id(mrb, e, MRB_SYM(UTF_8), mrb_str_new_lit_frozen(mrb, ENC_UTF8)); +} + +void +mrb_mruby_encoding_gem_final(mrb_state* mrb) +{ +} diff --git a/mrbgems/mruby-encoding/test/numeric.rb b/mrbgems/mruby-encoding/test/numeric.rb new file mode 100644 index 000000000..f79235de0 --- /dev/null +++ b/mrbgems/mruby-encoding/test/numeric.rb @@ -0,0 +1,27 @@ +assert('Integer#chr') do + assert_equal("A", 65.chr) + assert_equal("B", 0x42.chr) + assert_equal("\xab", 171.chr) + assert_raise(RangeError) { -1.chr } + assert_raise(RangeError) { 256.chr } + + assert_equal("A", 65.chr("ASCII-8BIT")) + assert_equal("B", 0x42.chr("BINARY")) + assert_equal("\xab", 171.chr("ascii-8bit")) + assert_raise(RangeError) { -1.chr("binary") } + assert_raise(RangeError) { 256.chr("Ascii-8bit") } + assert_raise(ArgumentError) { 65.chr("ASCII") } + assert_raise(ArgumentError) { 65.chr("ASCII-8BIT", 2) } + assert_raise(TypeError) { 65.chr(:BINARY) } + + if __ENCODING__ == "ASCII-8BIT" + assert_raise(ArgumentError) { 65.chr("UTF-8") } + else + assert_equal("A", 65.chr("UTF-8")) + assert_equal("B", 0x42.chr("UTF-8")) + assert_equal("«", 171.chr("utf-8")) + assert_equal("あ", 12354.chr("Utf-8")) + assert_raise(RangeError) { -1.chr("utf-8") } + assert_raise(RangeError) { 0x110000.chr.chr("UTF-8") } + end +end diff --git a/mrbgems/mruby-encoding/test/string.rb b/mrbgems/mruby-encoding/test/string.rb new file mode 100644 index 000000000..0c54e6dcf --- /dev/null +++ b/mrbgems/mruby-encoding/test/string.rb @@ -0,0 +1,30 @@ +## +# String(Ext) Test + +UTF8STRING = __ENCODING__ == "UTF-8" + +assert('String#valid_encoding?') do + assert_true "hello".valid_encoding? + if UTF8STRING + assert_true "あ".valid_encoding? + assert_false "\xfe".valid_encoding? + assert_false "あ\xfe".valid_encoding? + assert_true "あ\xfe".b.valid_encoding? + else + assert_true "\xfe".valid_encoding? + end +end + +assert('String#encoding') do + if UTF8STRING + a = "あ" + assert_equal Encoding::UTF_8, a.encoding + assert_equal Encoding::BINARY, a.b.encoding + assert_equal a, a.force_encoding(Encoding::BINARY) + assert_equal a, a.force_encoding(Encoding::BINARY) + assert_equal Encoding::BINARY, a.encoding + else + a = "hello" + assert_equal Encoding::BINARY, a.encoding + end +end