* pack.c (pack_pack): support endian modifiers: < and >.

[ruby-dev:42376] Feature #3491

* pack.c (pack_unpack): ditto.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@29496 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
naruse 2010-10-14 13:12:56 +00:00
parent b1085abaeb
commit 5825359dd8
4 changed files with 163 additions and 86 deletions

View File

@ -1,3 +1,10 @@
Thu Oct 14 20:41:27 2010 NARUSE, Yui <naruse@ruby-lang.org>
* pack.c (pack_pack): support endian modifiers: < and >.
[ruby-dev:42376] Feature #3491
* pack.c (pack_unpack): ditto.
Thu Oct 14 20:50:51 2010 Masaki Suketa <masaki.suketa@nifty.ne.jp>
* ext/win32ole/win32ole.c (reg_get_val): expand environment in

6
NEWS
View File

@ -31,6 +31,8 @@ with all sufficient information, see the ChangeLog file.
* new constants:
* File::NULL
name of NULL device.
* extended methods:
* String#unpack supports endian modifiers
* String
* new methods:
@ -44,6 +46,10 @@ with all sufficient information, see the ChangeLog file.
* extended methods:
* IO#putc supports multibyte characters
* Array
* extended methods:
* Array#pack supports endian modifiers
* io/console
* new methods:
* IO#noecho {|io| }

101
pack.c
View File

@ -330,14 +330,6 @@ static unsigned long utf8_to_uv(const char*,long*);
* l | Integer | 32-bit signed, native endian (int32_t)
* q | Integer | 64-bit signed, native endian (int64_t)
* | |
* S_, S! | Integer | unsigned short, native endian
* I, I_, I! | Integer | unsigned int, native endian
* L_, L! | Integer | unsigned long, native endian
* | |
* s_, s! | Integer | signed short, native endian
* i, i_, i! | Integer | signed int, native endian
* l_, l! | Integer | signed long, native endian
* | |
* n | Integer | 16-bit unsigned, network (big-endian) byte order
* N | Integer | 32-bit unsigned, network (big-endian) byte order
* v | Integer | 16-bit unsigned, VAX (little-endian) byte order
@ -379,6 +371,14 @@ static unsigned long utf8_to_uv(const char*,long*);
* @ | --- | moves to absolute position
* X | --- | back up a byte
* x | --- | null byte
*
* | Target |
* Modifier | Directive | Meaning
* ---------------------------------------------------------------------------
* _, ! | sSiIlL | Force native size of the related type:
* | | short, int, long, and long long
* > | sSiIlLqQ | Force big-endian byte order
* < | sSiIlLqQ | Force little-endian byte order
*/
static VALUE
@ -396,6 +396,7 @@ pack_pack(VALUE ary, VALUE fmt)
int natint; /* native integer */
#endif
int signed_p, integer_size, bigendian_p;
int explicit_endian = 0;
StringValue(fmt);
p = RSTRING_PTR(fmt);
@ -425,19 +426,39 @@ pack_pack(VALUE ary, VALUE fmt)
}
continue;
}
if (*p == '_' || *p == '!') {
static const char natstr[] = "sSiIlL";
if (strchr(natstr, type)) {
{
static const char natstr[] = "sSiIlL";
static const char endstr[] = "sSiIlLqQ";
modifiers:
switch (*p) {
case '_':
case '!':
if (strchr(natstr, type)) {
#ifdef NATINT_PACK
natint = 1;
natint = 1;
#endif
p++;
}
else {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
p++;
}
else {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
}
goto modifiers;
case '<':
case '>':
if (!strchr(endstr, type)) {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, endstr);
}
if (explicit_endian) {
rb_raise(rb_eRangeError, "Can't use both '<' and '>'");
}
explicit_endian = *p++;
goto modifiers;
}
}
if (*p == '*') { /* set data length */
len = strchr("@Xxu", type) ? 0
: strchr("PMm", type) ? 1
@ -716,6 +737,10 @@ pack_pack(VALUE ary, VALUE fmt)
goto pack_integer;
pack_integer:
if (explicit_endian) {
bigendian_p = ((explicit_endian - '<') != 0);
}
switch (integer_size) {
#if defined(HAVE_INT16_T) && !defined(FORCE_BIG_PACK)
case SIZEOF_INT16_T:
@ -1309,6 +1334,7 @@ pack_unpack(VALUE str, VALUE fmt)
#endif
int block_p = rb_block_given_p();
int signed_p, integer_size, bigendian_p;
int explicit_endian = 0;
#define UNPACK_PUSH(item) do {\
VALUE item_val = (item);\
if (block_p) {\
@ -1340,20 +1366,41 @@ pack_unpack(VALUE str, VALUE fmt)
}
continue;
}
star = 0;
if (*p == '_' || *p == '!') {
static const char natstr[] = "sSiIlL";
if (strchr(natstr, type)) {
star = 0;
{
static const char natstr[] = "sSiIlL";
static const char endstr[] = "sSiIlLqQ";
modifiers:
switch (*p) {
case '_':
case '!':
if (strchr(natstr, type)) {
#ifdef NATINT_PACK
natint = 1;
natint = 1;
#endif
p++;
}
else {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
p++;
}
else {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
}
goto modifiers;
case '<':
case '>':
if (!strchr(endstr, type)) {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, endstr);
}
if (explicit_endian) {
rb_raise(rb_eRangeError, "Can't use both '<' and '>'");
}
explicit_endian = *p++;
goto modifiers;
}
}
if (p >= pend)
len = 1;
else if (*p == '*') {
@ -1586,6 +1633,10 @@ pack_unpack(VALUE str, VALUE fmt)
goto unpack_integer;
unpack_integer:
if (explicit_endian) {
bigendian_p = ((explicit_endian - '<') != 0);
}
switch (integer_size) {
#if defined(HAVE_INT16_T) && !defined(FORCE_BIG_PACK)
case SIZEOF_INT16_T:

View File

@ -70,75 +70,88 @@ class TestPack < Test::Unit::TestCase
assert_equal [1,1,1], "\000\000\000\001\000\000\000\001\000\000\000\001".unpack('N*')
end
def _integer_big_endian(mod='')
assert_equal("\x01\x02", [0x0102].pack("s"+mod))
assert_equal("\x01\x02", [0x0102].pack("S"+mod))
assert_equal("\x01\x02\x03\x04", [0x01020304].pack("l"+mod))
assert_equal("\x01\x02\x03\x04", [0x01020304].pack("L"+mod))
assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("q"+mod))
assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("Q"+mod))
assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("s!"+mod))
assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("S!"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i!"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I!"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("l!"+mod))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("L!"+mod))
%w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
fmt += mod
nuls = [0].pack(fmt)
v = 0
s = "".force_encoding("ascii-8bit")
nuls.bytesize.times {|i|
j = i + 40
v = v * 256 + j
s << [j].pack("C")
}
assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
s2 = s+s
fmt2 = fmt+"*"
assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
}
end
def _integer_little_endian(mod='')
assert_equal("\x02\x01", [0x0102].pack("s"+mod))
assert_equal("\x02\x01", [0x0102].pack("S"+mod))
assert_equal("\x04\x03\x02\x01", [0x01020304].pack("l"+mod))
assert_equal("\x04\x03\x02\x01", [0x01020304].pack("L"+mod))
assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("q"+mod))
assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("Q"+mod))
assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("s!"+mod))
assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("S!"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i!"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I!"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("l!"+mod))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("L!"+mod))
%w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
fmt += mod
nuls = [0].pack(fmt)
v = 0
s = "".force_encoding("ascii-8bit")
nuls.bytesize.times {|i|
j = i+40
v = v * 256 + j
s << [j].pack("C")
}
s.reverse!
assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
s2 = s+s
fmt2 = fmt+"*"
assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
}
end
def test_integer_endian
s = [1].pack("s")
assert_includes(["\0\1", "\1\0"], s)
if s == "\0\1"
# big endian
assert_equal("\x01\x02", [0x0102].pack("s"))
assert_equal("\x01\x02", [0x0102].pack("S"))
assert_equal("\x01\x02\x03\x04", [0x01020304].pack("l"))
assert_equal("\x01\x02\x03\x04", [0x01020304].pack("L"))
assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("q"))
assert_equal("\x01\x02\x03\x04\x05\x06\x07\x08", [0x0102030405060708].pack("Q"))
assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("s!"))
assert_match(/\A\x00*\x01\x02\z/, [0x0102].pack("S!"))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i"))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I"))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("i!"))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("I!"))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("l!"))
assert_match(/\A\x00*\x01\x02\x03\x04\z/, [0x01020304].pack("L!"))
%w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
nuls = [0].pack(fmt)
v = 0
s = "".force_encoding("ascii-8bit")
nuls.bytesize.times {|i|
j = i + 40
v = v * 256 + j
s << [j].pack("C")
}
assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
s2 = s+s
fmt2 = fmt+"*"
assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
}
_integer_big_endian()
else
# little endian
assert_equal("\x02\x01", [0x0102].pack("s"))
assert_equal("\x02\x01", [0x0102].pack("S"))
assert_equal("\x04\x03\x02\x01", [0x01020304].pack("l"))
assert_equal("\x04\x03\x02\x01", [0x01020304].pack("L"))
assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("q"))
assert_equal("\x08\x07\x06\x05\x04\x03\x02\x01", [0x0102030405060708].pack("Q"))
assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("s!"))
assert_match(/\A\x02\x01\x00*\z/, [0x0102].pack("S!"))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i"))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I"))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("i!"))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("I!"))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("l!"))
assert_match(/\A\x04\x03\x02\x01\x00*\z/, [0x01020304].pack("L!"))
%w[s S l L q Q s! S! i I i! I! l! L!].each {|fmt|
nuls = [0].pack(fmt)
v = 0
s = "".force_encoding("ascii-8bit")
nuls.bytesize.times {|i|
j = i+40
v = v * 256 + j
s << [j].pack("C")
}
s.reverse!
assert_equal(s, [v].pack(fmt), "[#{v}].pack(#{fmt.dump})")
assert_equal([v], s.unpack(fmt), "#{s.dump}.unpack(#{fmt.dump})")
s2 = s+s
fmt2 = fmt+"*"
assert_equal([v,v], s2.unpack(fmt2), "#{s2.dump}.unpack(#{fmt2.dump})")
}
_integer_little_endian()
end
end
def test_integer_endian_explicit
_integer_big_endian('>')
_integer_little_endian('<')
end
def test_pack_U
assert_raise(RangeError) { [-0x40000001].pack("U") }
assert_raise(RangeError) { [-0x40000000].pack("U") }