aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorusa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-12-01 13:08:20 +0000
committerusa <usa@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-12-01 13:08:20 +0000
commit001b9a1ef694d634549a34480205ecc1d98d9026 (patch)
tree58a26d5d86ba98caa360b4d319e5092f6a5ba2b1
parent64602e56e80f99d9de5f6fcd4068d80724498271 (diff)
downloadruby-001b9a1ef694d634549a34480205ecc1d98d9026.tar.gz
Supports `buffer` and `offset` in `Array#pack`
* pack.c (pack_pack): Supports `buffer` and `offset` in `Array#pack`. [Feature #12754] [ruby-dev:49798] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@56957 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--NEWS4
-rw-r--r--pack.c61
-rw-r--r--test/ruby/test_pack.rb34
3 files changed, 94 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index e4ed0f2328..2eb751cbab 100644
--- a/NEWS
+++ b/NEWS
@@ -41,6 +41,10 @@ with all sufficient information, see the ChangeLog file or Redmine
* 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]
diff --git a/pack.c b/pack.c
index 4e1198b635..fbbf0b81cd 100644
--- a/pack.c
+++ b/pack.c
@@ -140,7 +140,9 @@ rb_str_associated(VALUE str)
/*
* 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)
* 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)
*/
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)
#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)
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();
diff --git a/test/ruby/test_pack.rb b/test/ruby/test_pack.rb
index c5af6f2152..415dc78e60 100644
--- a/test/ruby/test_pack.rb
+++ b/test/ruby/test_pack.rb
@@ -812,4 +812,38 @@ EXPECTED
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