ruby-changes:44884
From: usa <ko1@a...>
Date: Thu, 1 Dec 2016 22:08:24 +0900 (JST)
Subject: [ruby-changes:44884] usa:r56957 (trunk): Supports `buffer` and `offset` in `Array#pack`
usa 2016-12-01 22:08:20 +0900 (Thu, 01 Dec 2016) New Revision: 56957 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=56957 Log: Supports `buffer` and `offset` in `Array#pack` * pack.c (pack_pack): Supports `buffer` and `offset` in `Array#pack`. [Feature #12754] [ruby-dev:49798] Modified files: trunk/NEWS trunk/pack.c trunk/test/ruby/test_pack.rb Index: NEWS =================================================================== --- NEWS (revision 56956) +++ NEWS (revision 56957) @@ -41,6 +41,10 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L41 * Array#concat [Feature #12333] Now takes multiple arguments. + * Array#pack [Feature #12754] + Now takes optional arguments `buffer' and `offset' to reuse already + allocated buffer. + * Comparable * Comparable#clamp. [Feature #10594] Index: pack.c =================================================================== --- pack.c (revision 56956) +++ pack.c (revision 56957) @@ -140,7 +140,9 @@ rb_str_associated(VALUE str) https://github.com/ruby/ruby/blob/trunk/pack.c#L140 /* * call-seq: - * arr.pack ( aTemplateString ) -> aBinaryString + * arr.pack( aTemplateString ) -> aBinaryString + * arr.pack( aTemplateString, buffer: aBufferString ) -> aBufferString + * arr.pack( aTemplateString, buffer: aBufferString, offset: offsetOfBuffer ) -> aBufferString * * Packs the contents of <i>arr</i> into a binary sequence according to * the directives in <i>aTemplateString</i> (see the table below) @@ -162,6 +164,18 @@ rb_str_associated(VALUE str) https://github.com/ruby/ruby/blob/trunk/pack.c#L164 * a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000" * n.pack("ccc") #=> "ABC" * + * If <i>aBufferString</i> is specified and its capacity is enough, + * +pack+ uses it as the buffer and returns it. + * User can also specify <i>offsetOfBuffer</i>, then the result of +pack+ + * is filled after <i>offsetOfBuffer</i> bytes. + * If original contents of <i>aBufferString</i> exists and it's longer than + * <i>offsetOfBuffer</i>, the rest of <i>offsetOfBuffer</i> are cut. + * If it's shorter, the gap is filled with ``<code>\0</code>''. + * + * Note that ``buffer:'' option does not guarantee not to allocate memory + * in +pack+. If the capacity of <i>aBufferString</i> is not enough, + * +pack+ allocates memory. + * * Directives for +pack+. * * Integer | Array | @@ -255,12 +269,12 @@ rb_str_associated(VALUE str) https://github.com/ruby/ruby/blob/trunk/pack.c#L269 */ static VALUE -pack_pack(VALUE ary, VALUE fmt) +pack_pack(int argc, VALUE *argv, VALUE ary) { static const char nul10[] = "\0\0\0\0\0\0\0\0\0\0"; static const char spc10[] = " "; const char *p, *pend; - VALUE res, from, associates = 0; + VALUE fmt, opt = Qnil, res, from, associates = 0, buffer = 0; char type; long len, idx, plen; const char *ptr; @@ -270,10 +284,47 @@ pack_pack(VALUE ary, VALUE fmt) https://github.com/ruby/ruby/blob/trunk/pack.c#L284 #endif int integer_size, bigendian_p; + rb_scan_args(argc, argv, "10:", &fmt, &opt); + StringValue(fmt); p = RSTRING_PTR(fmt); pend = p + RSTRING_LEN(fmt); - res = rb_str_buf_new(0); + if (!NIL_P(opt)) { + static ID keyword_ids[2]; + VALUE kwargs[2]; + VALUE voff; + long offset; + + if (!keyword_ids[0]) { + CONST_ID(keyword_ids[0], "buffer"); + CONST_ID(keyword_ids[1], "offset"); + } + + rb_get_kwargs(opt, keyword_ids, 0, 2, kwargs); + buffer = kwargs[0]; + voff = kwargs[1]; + + if (buffer == Qundef && !NIL_P(voff)) + rb_raise(rb_eArgError, "offset is specified without buffer"); + if (buffer != Qundef) { + long len; + if (!RB_TYPE_P(buffer, T_STRING)) + rb_raise(rb_eTypeError, "buffer must be String, not %s", rb_obj_classname(buffer)); + if (voff != Qundef) { + offset = NUM2LONG(voff); + if (rb_str_capacity(buffer) < offset) + rb_raise(rb_eArgError, "buffer is too small for offset"); + len = RSTRING_LEN(buffer); + rb_str_set_len(buffer, offset); + if (len < offset) + memset(RSTRING_PTR(buffer) + len, '\0', offset - len); + } + } + } + if (buffer) + res = buffer; + else + res = rb_str_buf_new(0); idx = 0; @@ -1948,7 +1999,7 @@ utf8_to_uv(const char *p, long *lenp) https://github.com/ruby/ruby/blob/trunk/pack.c#L1999 void Init_pack(void) { - rb_define_method(rb_cArray, "pack", pack_pack, 1); + rb_define_method(rb_cArray, "pack", pack_pack, -1); rb_define_method(rb_cString, "unpack", pack_unpack, 1); id_associated = rb_make_internal_id(); Index: test/ruby/test_pack.rb =================================================================== --- test/ruby/test_pack.rb (revision 56956) +++ test/ruby/test_pack.rb (revision 56957) @@ -812,4 +812,38 @@ EXPECTED https://github.com/ruby/ruby/blob/trunk/test/ruby/test_pack.rb#L812 assert_raise_with_message(ArgumentError, /too few/) {ary.pack("AA")} end; end + + def test_pack_with_buffer + buf = String.new(capacity: 100) + + assert_raise_with_message(ArgumentError, /without buffer/) { + [0xDEAD_BEEF].pack('N', offset: 10) + } + assert_raise_with_message(ArgumentError, /too small/) { + [0xDEAD_BEEF].pack('N', buffer: buf, offset: 200) + } + assert_raise_with_message(RuntimeError, /frozen/) { + [0xDEAD_BEEF].pack('N', buffer: 'foo'.freeze) + } + assert_raise_with_message(TypeError, /into Integer/) { + [0xDEAD_BEEF].pack('N', buffer: buf, offset: '10') + } + assert_raise_with_message(TypeError, /must be String/) { + [0xDEAD_BEEF].pack('N', buffer: Object.new) + } + + addr = [buf].pack('p') + + [0xDEAD_BEEF].pack('N', buffer: buf) + assert_equal "\xDE\xAD\xBE\xEF", buf + + [0xBABE_F00D].pack('N', buffer: buf, offset: 4) + assert_equal "\xDE\xAD\xBE\xEF\xBA\xBE\xF0\x0D", buf + assert_equal addr, [buf].pack('p') + + [0xBAAD_FACE].pack('N', buffer: buf, offset: 10) + assert_equal "\xDE\xAD\xBE\xEF\xBA\xBE\xF0\x0D\0\0\xBA\xAD\xFA\xCE", buf + + assert_equal addr, [buf].pack('p') + end end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/