[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]