Commit a5412d48 authored by Yukihiro "Matz" Matsumoto's avatar Yukihiro "Matz" Matsumoto

Add 'mrbgems/mruby-pack/' from commit '383a9c79'

git-subtree-dir: mrbgems/mruby-pack
git-subtree-mainline: 842e6945
git-subtree-split: 383a9c79
parents 842e6945 383a9c79
gem_*
gem-*
mrb-*.a
src/*.o
/tmp
script:
- "ruby run_test.rb all test"
mruby-pack (pack / unpack)
=========
mruby-pack provides `Array#pack` and `String#unpack` for mruby.
## Installation
Add the line below into your `build_config.rb`:
```
conf.gem :github => 'iij/mruby-pack'
```
There is no dependency on other mrbgems.
## Supported template string
- A : arbitrary binary string (space padded, count is width)
- a : arbitrary binary string (null padded, count is width)
- C : 8-bit unsigned (unsigned char)
- c : 8-bit signed (signed char)
- D, d: 64-bit float, native format
- E : 64-bit float, little endian byte order
- e : 32-bit float, little endian byte order
- F, f: 32-bit float, native format
- G : 64-bit float, network (big-endian) byte order
- g : 32-bit float, network (big-endian) byte order
- H : hex string (high nibble first)
- h : hex string (low nibble first)
- I : unsigned integer, native endian (`unsigned int` in C)
- i : signed integer, native endian (`int` in C)
- L : 32-bit unsigned, native endian (`uint32_t`)
- l : 32-bit signed, native endian (`int32_t`)
- m : base64 encoded string (see RFC 2045, count is width)
- N : 32-bit unsigned, network (big-endian) byte order
- n : 16-bit unsigned, network (big-endian) byte order
- Q : 64-bit unsigned, native endian (`uint64_t`)
- q : 64-bit signed, native endian (`int64_t`)
- S : 16-bit unsigned, native endian (`uint16_t`)
- s : 16-bit signed, native endian (`int16_t`)
- U : UTF-8 character
- V : 32-bit unsigned, VAX (little-endian) byte order
- v : 16-bit unsigned, VAX (little-endian) byte order
- x : null byte
- Z : same as "a", except that null is added with *
## License
Copyright (c) 2012 Internet Initiative Japan Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
MRuby::Gem::Specification.new('mruby-pack') do |spec|
spec.license = 'MIT'
spec.authors = 'Internet Initiative Japan Inc.'
spec.cc.include_paths << "#{build.root}/src"
end
# encoding: ascii
# a = Array, s = String, t = Template
def packtest(a, s, t)
begin
r = a.pack(t)
return if r == s
puts "#{a.inspect}.pack(#{t.inspect}) -> #{r.inspect} should be #{s.inspect}"
rescue => r
unless r.is_a? s
puts "#{a.inspect}.pack(#{t.inspect}) -> #{r.inspect} should be #{s.inspect}"
end
end
end
def unpacktest(a, s, t)
r = s.unpack(t)
return if r == a
puts "#{s.inspect}.unpack(#{t.inspect}) -> #{r.inspect} should be #{a.inspect}"
end
def pptest(a, s, t)
packtest(a, s, t)
unpacktest(a, s, t)
end
pptest [1], "\x01", "C"
packtest [1.1], "\x01", "C"
packtest [-1], "\xff", "C"
packtest [1,2], "\x01\x02", "C2"
#packtest [1], "X", ArgumentError
unpacktest [48, nil], "0", "CC"
unpacktest [160, -96], "\xa0\xa0", "Cc"
unpacktest [49, 50, 51], "123", "C*"
pptest [12849], "12", "S"
unpacktest [nil], "0", "S"
unpacktest [12849, nil], "123", "SS"
unpacktest [12849], "123", "S*"
pptest [10000], "\x27\x10", "s>"
pptest [-10000], "\xd8\xf0", "s>"
pptest [50000], "\xc3\x50", "S>"
pptest [10000], "\x10\x27", "s<"
pptest [-10000], "\xf0\xd8", "s<"
pptest [50000], "\x50\xc3", "S<"
pptest [1000000000], "\x3b\x9a\xca\x00", "l>"
pptest [-1000000000], "\xc4\x65\x36\x00", "l>"
pptest [1], "\x01\x00\x00\x00", "L<"
pptest [258], "\x02\x01\x00\x00", "L<"
pptest [66051], "\x03\x02\x01\x00", "L<"
pptest [16909060], "\x04\x03\x02\x01", "L<"
pptest [16909060], "\x01\x02\x03\x04", "L>"
packtest [-1], "\xff\xff\xff\xff", "L<"
pptest [1000000000], "\x00\x00\x00\x00\x3b\x9a\xca\x00", "q>"
pptest [-1000000000], "\xff\xff\xff\xff\xc4\x65\x36\x00", "q>"
if (2**33).is_a? Fixnum
pptest [81985529216486895], "\x01\x23\x45\x67\x89\xab\xcd\xef", "q>"
pptest [-1167088121787636991], "\x01\x23\x45\x67\x89\xab\xcd\xef", "q<"
end
pptest [16909060], "\x01\x02\x03\x04", "N"
pptest [258], "\x01\x02", "n"
pptest [32769], "\x80\x01", "n"
pptest [16909060], "\x04\x03\x02\x01", "V"
pptest [258], "\x02\x01", "v"
packtest [""], "", "m"
packtest ["a"], "YQ==\n", "m"
packtest ["ab"], "YWI=\n", "m"
packtest ["abc"], "YWJj\n", "m"
packtest ["abcd"], "YWJjZA==\n", "m"
unpacktest [""], "", "m"
unpacktest ["a"], "YQ==\n", "m"
unpacktest ["ab"], "YWI=\n", "m"
unpacktest ["abc"], "YWJj\n", "m"
unpacktest ["abcd"], "YWJjZA==\n", "m"
packtest [""], "\0", "H"
packtest ["3"], "0", "H"
packtest ["34"], "", "H0"
packtest ["34"], "0", "H"
packtest ["34"], "4", "H2"
packtest ["34"], "4\0", "H3"
packtest ["3456"], "4P", "H3"
packtest ["34563"], "4V0", "H*"
packtest ["5a"], "Z", "H*"
packtest ["5A"], "Z", "H*"
unpacktest [""], "", "H"
unpacktest [""], "0", "H0"
unpacktest ["3"], "0", "H"
unpacktest ["30"], "0", "H2"
unpacktest ["30"], "0", "H3"
unpacktest ["303"], "01", "H3"
unpacktest ["303132"], "012", "H*"
unpacktest ["3031", 50], "012", "H4C"
unpacktest ["5a"], "Z", "H*"
packtest [""], "\0", "h"
packtest ["3"], "\03", "h"
packtest ["34"], "", "h0"
packtest ["34"], "\03", "h"
packtest ["34"], "C", "h2"
packtest ["34"], "C\0", "h3"
packtest ["3456"], "C\05", "h3"
packtest ["34563"], "Ce\03", "h*"
packtest [""], " ", "A"
unpacktest [""], "", "A"
pptest ["1"], "1", "A"
pptest ["1"], "1 ", "A2"
unpacktest ["1"], "1", "A2"
unpacktest ["1"], "1 ", "A2"
unpacktest ["1"], "1\0", "A2"
packtest ["12"], "1", "A"
unpacktest ["1"], "12", "A"
pptest ["123"], "123", "A*"
packtest ["1","2"], "2", "A0A"
unpacktest ["","2"], "2", "A0A"
packtest [""], "\0", "a"
unpacktest [""], "", "a"
pptest ["1"], "1", "a"
pptest ["1 "], "1 ", "a2"
pptest ["1\0"], "1\0", "a2"
packtest ["1"], "1\0", "a2"
pptest ["123"], "123", "a*"
packtest [""], "\0", "Z"
unpacktest [""], "", "Z"
pptest ["1"], "1", "Z"
pptest ["1"], "1\0", "Z2"
pptest ["1 "], "1 ", "Z2"
pptest ["123"], "123\0", "Z*"
pptest ["1","2"], "12", "ZZ"
pptest ["1","2"], "1\0002", "Z*Z"
unpacktest ["1","3"], "1\00023", "Z3Z"
packtest [1, 2], "\x01\x02", "CyC"
packtest [65], "A", 'U'
packtest [59411], "\xEE\xA0\x93", 'U'
pptest [1], "\x00\x01", "xC"
unpacktest [2], "\xcc\x02", "xC"
#!/usr/bin/env ruby
#
# mrbgems test runner
#
gemname = File.basename(File.dirname(File.expand_path __FILE__))
if __FILE__ == $0
repository, dir = 'https://github.com/mruby/mruby.git', 'tmp/mruby'
build_args = ARGV
build_args = ['all', 'test'] if build_args.nil? or build_args.empty?
Dir.mkdir 'tmp' unless File.exist?('tmp')
unless File.exist?(dir)
system "git clone #{repository} #{dir}"
end
exit system(%Q[cd #{dir}; MRUBY_CONFIG=#{File.expand_path __FILE__} ruby minirake #{build_args.join(' ')}])
end
MRuby::Build.new do |conf|
toolchain :gcc
conf.gembox 'default'
conf.gem File.expand_path(File.dirname(__FILE__))
end
/*
** pack.c - Array#pack, String#unpack
*/
#include "mruby.h"
#include "error.h"
#include "mruby/array.h"
#include "mruby/class.h"
#include "mruby/numeric.h"
#include "mruby/string.h"
#include "mruby/variable.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
struct tmpl {
mrb_value str;
int idx;
};
enum {
PACK_DIR_CHAR, /* C */
PACK_DIR_SHORT, /* S */
PACK_DIR_LONG, /* L */
PACK_DIR_QUAD, /* Q */
//PACK_DIR_INT, /* i */
//PACK_DIR_VAX,
PACK_DIR_UTF8, /* U */
//PACK_DIR_BER,
PACK_DIR_DOUBLE, /* E */
PACK_DIR_FLOAT, /* f */
PACK_DIR_STR, /* A */
PACK_DIR_HEX, /* h */
PACK_DIR_BASE64, /* m */
PACK_DIR_NUL, /* x */
PACK_DIR_INVALID
};
enum {
PACK_TYPE_INTEGER,
PACK_TYPE_FLOAT,
PACK_TYPE_STRING,
PACK_TYPE_NONE
};
#define PACK_FLAG_s 0x00000001 /* native size ("_" "!") */
#define PACK_FLAG_a 0x00000002 /* null padding ("a") */
#define PACK_FLAG_Z 0x00000004 /* append nul char ("z") */
#define PACK_FLAG_SIGNED 0x00000008 /* native size ("_" "!") */
#define PACK_FLAG_GT 0x00000010 /* big endian (">") */
#define PACK_FLAG_LT 0x00000020 /* little endian ("<") */
#define PACK_FLAG_WIDTH 0x00000040 /* "count" is "width" */
#define PACK_FLAG_LSB 0x00000080 /* LSB / low nibble first */
#define PACK_FLAG_COUNT2 0x00000100 /* "count" is special... */
#define PACK_FLAG_LITTLEENDIAN 0x00000200 /* little endian actually */
#define PACK_BASE64_IGNORE 0xff
#define PACK_BASE64_PADDING 0xfe
static int littleendian = 0;
const static unsigned char base64chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static signed char base64_dec_tab[128];
static int
check_little_endian(void)
{
unsigned int n = 1;
return (*(unsigned char *)&n == 1);
}
static unsigned int
hex2int(unsigned char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'A' && ch <= 'F')
return 10 + (ch - 'A');
else if (ch >= 'a' && ch <= 'f')
return 10 + (ch - 'a');
else
return 0;
}
static void
make_base64_dec_tab(void)
{
int i;
memset(base64_dec_tab, PACK_BASE64_IGNORE, sizeof(base64_dec_tab));
for (i = 0; i < 26; i++)
base64_dec_tab['A' + i] = i;
for (i = 0; i < 26; i++)
base64_dec_tab['a' + i] = i + 26;
for (i = 0; i < 10; i++)
base64_dec_tab['0' + i] = i + 52;
base64_dec_tab['+'] = 62;
base64_dec_tab['/'] = 63;
base64_dec_tab['='] = PACK_BASE64_PADDING;
}
static mrb_value
str_len_ensure(mrb_state *mrb, mrb_value str, int len)
{
int n = RSTRING_LEN(str);
if (len > n) {
do {
n *= 2;
} while (len > n);
str = mrb_str_resize(mrb, str, n);
}
return str;
}
static int
pack_c(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
{
str = str_len_ensure(mrb, str, sidx + 1);
RSTRING_PTR(str)[sidx] = mrb_fixnum(o);
return 1;
}
static int
unpack_c(mrb_state *mrb, const void *src, int srclen, mrb_value ary, unsigned int flags)
{
if (flags & PACK_FLAG_SIGNED)
mrb_ary_push(mrb, ary, mrb_fixnum_value(*(signed char *)src));
else
mrb_ary_push(mrb, ary, mrb_fixnum_value(*(unsigned char *)src));
return 1;
}
static int
pack_s(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
{
unsigned short n;
str = str_len_ensure(mrb, str, sidx + 2);
n = mrb_fixnum(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
RSTRING_PTR(str)[sidx+0] = n % 256;
RSTRING_PTR(str)[sidx+1] = n / 256;
} else {
RSTRING_PTR(str)[sidx+0] = n / 256;
RSTRING_PTR(str)[sidx+1] = n % 256;
}
return 2;
}
static int
unpack_s(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
{
int n;
if (flags & PACK_FLAG_LITTLEENDIAN) {
n = src[1] * 256 + src[0];
} else {
n = src[0] * 256 + src[1];
}
if ((flags & PACK_FLAG_SIGNED) && (n >= 0x8000)) {
n -= 0x10000;
}
mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
return 2;
}
static int
pack_l(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
{
unsigned long n;
str = str_len_ensure(mrb, str, sidx + 4);
n = mrb_fixnum(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
RSTRING_PTR(str)[sidx+0] = n & 0xff;
RSTRING_PTR(str)[sidx+1] = n >> 8;
RSTRING_PTR(str)[sidx+2] = n >> 16;
RSTRING_PTR(str)[sidx+3] = n >> 24;
} else {
RSTRING_PTR(str)[sidx+0] = n >> 24;
RSTRING_PTR(str)[sidx+1] = n >> 16;
RSTRING_PTR(str)[sidx+2] = n >> 8;
RSTRING_PTR(str)[sidx+3] = n & 0xff;
}
return 4;
}
static int
unpack_l(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
{
char msg[60];
uint32_t ul;
mrb_int n;
if (flags & PACK_FLAG_LITTLEENDIAN) {
ul = (uint32_t)src[3] * 256*256*256;
ul += (uint32_t)src[2] *256*256;
ul += (uint32_t)src[1] *256;
ul += (uint32_t)src[0];
} else {
ul = (uint32_t)src[0] * 256*256*256;
ul += (uint32_t)src[1] *256*256;
ul += (uint32_t)src[2] *256;
ul += (uint32_t)src[3];
}
if (flags & PACK_FLAG_SIGNED) {
int32_t sl = ul;
if (!FIXABLE(sl)) {
snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %ld", (long)sl);
mrb_raise(mrb, E_RANGE_ERROR, msg);
}
n = sl;
} else {
if (!POSFIXABLE(ul)) {
snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %lu", (unsigned long)ul);
mrb_raise(mrb, E_RANGE_ERROR, msg);
}
n = ul;
}
mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
return 4;
}
static int
pack_q(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
{
unsigned long long n;
str = str_len_ensure(mrb, str, sidx + 8);
n = mrb_fixnum(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
RSTRING_PTR(str)[sidx+0] = n & 0xff;
RSTRING_PTR(str)[sidx+1] = n >> 8;
RSTRING_PTR(str)[sidx+2] = n >> 16;
RSTRING_PTR(str)[sidx+3] = n >> 24;
RSTRING_PTR(str)[sidx+4] = n >> 32;
RSTRING_PTR(str)[sidx+5] = n >> 40;
RSTRING_PTR(str)[sidx+6] = n >> 48;
RSTRING_PTR(str)[sidx+7] = n >> 56;
} else {
RSTRING_PTR(str)[sidx+0] = n >> 56;
RSTRING_PTR(str)[sidx+1] = n >> 48;
RSTRING_PTR(str)[sidx+2] = n >> 40;
RSTRING_PTR(str)[sidx+3] = n >> 32;
RSTRING_PTR(str)[sidx+4] = n >> 24;
RSTRING_PTR(str)[sidx+5] = n >> 16;
RSTRING_PTR(str)[sidx+6] = n >> 8;
RSTRING_PTR(str)[sidx+7] = n & 0xff;
}
return 8;
}
static int
unpack_q(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
{
char msg[60];
uint64_t ull;
int i, pos, step;
mrb_int n;
if (flags & PACK_FLAG_LITTLEENDIAN) {
pos = 7;
step = -1;
} else {
pos = 0;
step = 1;
}
ull = 0;
for (i = 0; i < 8; i++) {
ull = ull * 256 + (uint64_t)src[pos];
pos += step;
}
if (flags & PACK_FLAG_SIGNED) {
int64_t sll = ull;
if (!FIXABLE(sll)) {
snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %lld", (long long)sll);
mrb_raise(mrb, E_RANGE_ERROR, msg);
}
n = sll;
} else {
if (!POSFIXABLE(ull)) {
snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %llu", (unsigned long long)ull);
mrb_raise(mrb, E_RANGE_ERROR, msg);
}
n = ull;
}
mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
return 8;
}
static int
pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
{
int i;
double d;
uint8_t *buffer = (uint8_t *)&d;
str = str_len_ensure(mrb, str, sidx + 8);
d = mrb_float(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
#ifdef MRB_ENDIAN_BIG
for (i = 0; i < 8; ++i) {
RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
}
#else
memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
#endif
} else {
#ifdef MRB_ENDIAN_BIG
memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
#else
for (i = 0; i < 8; ++i) {
RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
}
#endif
}
return 8;
}
static int
unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
{
int i;
double d;
uint8_t *buffer = (uint8_t *)&d;
if (flags & PACK_FLAG_LITTLEENDIAN) {
#ifdef MRB_ENDIAN_BIG
for (i = 0; i < 8; ++i) {
buffer[8 - i - 1] = src[i];
}
#else
memcpy(buffer, src, 8);
#endif
} else {
#ifdef MRB_ENDIAN_BIG
memcpy(buffer, src, 8);
#else
for (i = 0; i < 8; ++i) {
buffer[8 - i - 1] = src[i];
}
#endif
}
mrb_ary_push(mrb, ary, mrb_float_value(mrb, d));
return 8;
}
static int
pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
{
int i;
float f;
uint8_t *buffer = (uint8_t *)&f;
str = str_len_ensure(mrb, str, sidx + 4);
f = mrb_float(o);
if (flags & PACK_FLAG_LITTLEENDIAN) {
#ifdef MRB_ENDIAN_BIG
for (i = 0; i < 4; ++i) {
RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
}
#else
memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
#endif
} else {
#ifdef MRB_ENDIAN_BIG
memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
#else
for (i = 0; i < 4; ++i) {
RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
}
#endif
}
return 4;
}
static int
unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
{
int i;
float f;
uint8_t *buffer = (uint8_t *)&f;
if (flags & PACK_FLAG_LITTLEENDIAN) {
#ifdef MRB_ENDIAN_BIG
for (i = 0; i < 4; ++i) {
buffer[4 - i - 1] = src[i];
}
#else
memcpy(buffer, src, 4);
#endif
} else {
#ifdef MRB_ENDIAN_BIG
memcpy(buffer, src, 4);
#else
for (i = 0; i < 4; ++i) {
buffer[4 - i - 1] = src[i];
}
#endif
}
mrb_ary_push(mrb, ary, mrb_float_value(mrb, f));
return 4;
}
static int
pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count, unsigned int flags)
{
char utf8[4];
int len;
unsigned long c = 0;
if (mrb_float_p(o)) {
goto range_error;
}
c = mrb_fixnum(o);
/* Unicode character */
/* from mruby-compiler gem */
if (c < 0x80) {
utf8[0] = (char)c;
len = 1;
}
else if (c < 0x800) {
utf8[0] = (char)(0xC0 | (c >> 6));
utf8[1] = (char)(0x80 | (c & 0x3F));
len = 2;
}
else if (c < 0x10000) {
utf8[0] = (char)(0xE0 | (c >> 12) );
utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F));
utf8[2] = (char)(0x80 | ( c & 0x3F));
len = 3;
}
else if (c < 0x200000) {
utf8[0] = (char)(0xF0 | (c >> 18) );
utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F));
utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F));
utf8[3] = (char)(0x80 | ( c & 0x3F));
len = 4;
}
else {
range_error:
mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range");
}
str = str_len_ensure(mrb, str, sidx + len);
memcpy(RSTRING_PTR(str) + sidx, utf8, len);
return len;
}
static const unsigned long utf8_limits[] = {
0x0, /* 1 */
0x80, /* 2 */
0x800, /* 3 */
0x10000, /* 4 */
0x200000, /* 5 */
0x4000000, /* 6 */
0x80000000, /* 7 */
};
static unsigned long
utf8_to_uv(mrb_state *mrb, const char *p, long *lenp)
{
int c = *p++ & 0xff;
unsigned long uv = c;
long n;
if (!(uv & 0x80)) {
*lenp = 1;
return uv;
}
if (!(uv & 0x40)) {
*lenp = 1;
mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
}
if (!(uv & 0x20)) { n = 2; uv &= 0x1f; }
else if (!(uv & 0x10)) { n = 3; uv &= 0x0f; }
else if (!(uv & 0x08)) { n = 4; uv &= 0x07; }
else if (!(uv & 0x04)) { n = 5; uv &= 0x03; }
else if (!(uv & 0x02)) { n = 6; uv &= 0x01; }
else {
*lenp = 1;
mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
}
if (n > *lenp) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %S bytes, given %S bytes)",
mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
}
*lenp = n--;
if (n != 0) {
while (n--) {
c = *p++ & 0xff;
if ((c & 0xc0) != 0x80) {
*lenp -= n + 1;
mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
}
else {
c &= 0x3f;
uv = uv << 6 | c;
}
}
}
n = *lenp - 1;
if (uv < utf8_limits[n]) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "redundant UTF-8 sequence");
}
return uv;
}
static int
unpack_utf8(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
{
unsigned long uv;
long lenp = srclen;
if (srclen == 0) {
return 1;
}
uv = utf8_to_uv(mrb, (const char *)src, &lenp);
mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)uv));
return (int)lenp;
}
static int
pack_a(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
{
int copylen, slen, padlen;
char *dptr, *dptr0, pad, *sptr;
sptr = RSTRING_PTR(src);
slen = RSTRING_LEN(src);
if ((flags & PACK_FLAG_a) || (flags & PACK_FLAG_Z))
pad = '\0';
else
pad = ' ';
if (count == 0) {
return 0;
} else if (count == -1) {
copylen = slen;
padlen = (flags & PACK_FLAG_Z) ? 1 : 0;
} else if (count < slen) {
copylen = count;
padlen = 0;
} else {
copylen = slen;
padlen = count - slen;
}
dst = str_len_ensure(mrb, dst, didx + copylen + padlen);
dptr0 = dptr = RSTRING_PTR(dst) + didx;
memcpy(dptr, sptr, copylen);
dptr += copylen;
while (padlen-- > 0) {
*dptr++ = pad;
}
return dptr - dptr0;
}
static int
unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, unsigned int flags)
{
mrb_value dst;
const char *cp, *sptr;
long copylen;
sptr = (const char *)src;
if (count != -1 && count < slen) {
slen = count;
}
copylen = slen;
if (flags & PACK_FLAG_Z) { /* "Z" */
if ((cp = (const char *)memchr(sptr, '\0', slen)) != NULL) {
copylen = cp - sptr;
if (count == -1) {
slen = copylen + 1;
}
}
} else if (!(flags & PACK_FLAG_a)) { /* "A" */
while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) {
copylen--;
}
}
dst = mrb_str_new(mrb, sptr, copylen);
mrb_ary_push(mrb, ary, dst);
return slen;
}
static int
pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
{
unsigned int a, ashift, b, bshift;
int slen;
char *dptr, *dptr0, *sptr;
sptr = RSTRING_PTR(src);
slen = RSTRING_LEN(src);
if (flags & PACK_FLAG_LSB) {
ashift = 0;
bshift = 4;
} else {
ashift = 4;
bshift = 0;
}
if (count == -1) {
count = slen;
} else if (slen > count) {
slen = count;
}
dst = str_len_ensure(mrb, dst, didx + count);
dptr = RSTRING_PTR(dst) + didx;
dptr0 = dptr;
for (; count > 0; count -= 2) {
a = b = 0;
if (slen > 0) {
a = hex2int(*sptr++);
slen--;
}
if (slen > 0) {
b = hex2int(*sptr++);
slen--;
}
*dptr++ = (a << ashift) + (b << bshift);
}
return dptr - dptr0;
}
static int
unpack_h(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
{
mrb_value dst;
int a, ashift, b, bshift;
const char *sptr, *sptr0;
char *dptr, *dptr0;
const char hexadecimal[] = "0123456789abcdef";
if (flags & PACK_FLAG_LSB) {
ashift = 0;
bshift = 4;
} else {
ashift = 4;
bshift = 0;
}
sptr = (const char *)src;
if (count == -1)
count = slen * 2;
dst = mrb_str_new(mrb, NULL, count);
dptr = RSTRING_PTR(dst);
sptr0 = sptr;
dptr0 = dptr;
while (slen > 0 && count > 0) {
a = (*sptr >> ashift) & 0x0f;
b = (*sptr >> bshift) & 0x0f;
sptr++;
slen--;
*dptr++ = hexadecimal[a];
count--;
if (count > 0) {
*dptr++ = hexadecimal[b];
count--;
}
}
dst = mrb_str_resize(mrb, dst, dptr - dptr0);
mrb_ary_push(mrb, ary, dst);
return sptr - sptr0;
}
static int
pack_m(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
{
mrb_int dstlen;
unsigned long l;
int column, srclen;
char *srcptr, *dstptr, *dstptr0;
srcptr = RSTRING_PTR(src);
srclen = RSTRING_LEN(src);
if (srclen == 0) /* easy case */
return 0;
if (count != 0 && count < 3) { /* -1, 1 or 2 */
count = 45;
} else if (count >= 3) {
count -= count % 3;
}
dstlen = srclen / 3 * 4;
if (count > 0) {
dstlen += (srclen / count) + ((srclen % count) == 0 ? 0 : 1);
}
dst = str_len_ensure(mrb, dst, didx + dstlen);
dstptr = RSTRING_PTR(dst) + didx;
dstptr0 = dstptr;
for (column = 3; srclen >= 3; srclen -= 3, column += 3) {
l = (unsigned char)*srcptr++ << 16;
l += (unsigned char)*srcptr++ << 8;
l += (unsigned char)*srcptr++;
*dstptr++ = base64chars[(l >> 18) & 0x3f];
*dstptr++ = base64chars[(l >> 12) & 0x3f];
*dstptr++ = base64chars[(l >> 6) & 0x3f];
*dstptr++ = base64chars[ l & 0x3f];
if (column == count) {
*dstptr++ = '\n';
column = 0;
}
}
if (srclen == 1) {
l = (unsigned char)*srcptr++ << 16;
*dstptr++ = base64chars[(l >> 18) & 0x3f];
*dstptr++ = base64chars[(l >> 12) & 0x3f];
*dstptr++ = '=';
*dstptr++ = '=';
column += 3;
} else if (srclen == 2) {
l = (unsigned char)*srcptr++ << 16;
l += (unsigned char)*srcptr++ << 8;
*dstptr++ = base64chars[(l >> 18) & 0x3f];
*dstptr++ = base64chars[(l >> 12) & 0x3f];
*dstptr++ = base64chars[(l >> 6) & 0x3f];
*dstptr++ = '=';
column += 3;
}
if (column > 0 && count > 0) {
*dstptr++ = '\n';
}
return dstptr - dstptr0;
}
static int
unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int flags)
{
mrb_value dst;
int dlen;
unsigned long l;
int i, padding;
unsigned char c, ch[4];
const char *sptr, *sptr0;
char *dptr, *dptr0;
sptr0 = sptr = (const char *)src;
dlen = slen / 4 * 3; /* an estimated value - may be shorter */
dst = mrb_str_new(mrb, NULL, dlen);
dptr0 = dptr = RSTRING_PTR(dst);
padding = 0;
while (slen >= 4) {
for (i = 0; i < 4; i++) {
do {
if (slen-- == 0)
goto done;
c = *sptr++;
if (c >= sizeof(base64_dec_tab))
continue;
ch[i] = base64_dec_tab[c];
if (ch[i] == PACK_BASE64_PADDING) {
ch[i] = 0;
padding++;
}
} while (ch[i] == PACK_BASE64_IGNORE);
}
l = (ch[0] << 18) + (ch[1] << 12) + (ch[2] << 6) + ch[3];
if (padding == 0) {
*dptr++ = (l >> 16) & 0xff;
*dptr++ = (l >> 8) & 0xff;
*dptr++ = l & 0xff;
} else if (padding == 1) {
*dptr++ = (l >> 16) & 0xff;
*dptr++ = (l >> 8) & 0xff;
break;
} else {
*dptr++ = (l >> 16) & 0xff;
break;
}
}
done:
dst = mrb_str_resize(mrb, dst, dptr - dptr0);
mrb_ary_push(mrb, ary, dst);
return sptr - sptr0;
}
static int
pack_x(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
{
long i;
dst = str_len_ensure(mrb, dst, didx + count);
for (i = 0; i < count; i++) {
RSTRING_PTR(dst)[didx] = '\0';
}
return count;
}
static int
unpack_x(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
{
if (slen < count) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "x outside of string");
}
return count;
}
static void
prepare_tmpl(mrb_state *mrb, struct tmpl *tmpl)
{
mrb_get_args(mrb, "S", &tmpl->str);
tmpl->idx = 0;
}
static int
has_tmpl(const struct tmpl *tmpl)
{
return (tmpl->idx < RSTRING_LEN(tmpl->str));
}
static void
read_tmpl(mrb_state *mrb, struct tmpl *tmpl, int *dirp, int *typep, int *sizep, long *countp, unsigned int *flagsp)
{
int ch, dir, t, tlen, type;
int size = 0;
long count = 1;
unsigned int flags = 0;
const char *tptr;
tptr = RSTRING_PTR(tmpl->str);
tlen = RSTRING_LEN(tmpl->str);
t = tptr[tmpl->idx++];
alias:
switch (t) {
case 'A':
dir = PACK_DIR_STR;
type = PACK_TYPE_STRING;
flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
break;
case 'a':
dir = PACK_DIR_STR;
type = PACK_TYPE_STRING;
flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_a;
break;
case 'C':
dir = PACK_DIR_CHAR;
type = PACK_TYPE_INTEGER;
size = 1;
break;
case 'c':
dir = PACK_DIR_CHAR;
type = PACK_TYPE_INTEGER;
size = 1;
flags |= PACK_FLAG_SIGNED;
break;
case 'D': case 'd':
dir = PACK_DIR_DOUBLE;
type = PACK_TYPE_FLOAT;
size = 8;
flags |= PACK_FLAG_SIGNED;
break;
case 'F': case 'f':
dir = PACK_DIR_FLOAT;
type = PACK_TYPE_FLOAT;
size = 4;
flags |= PACK_FLAG_SIGNED;
break;
case 'E':
dir = PACK_DIR_DOUBLE;
type = PACK_TYPE_FLOAT;
size = 8;
flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT;
break;
case 'e':
dir = PACK_DIR_FLOAT;
type = PACK_TYPE_FLOAT;
size = 4;
flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT;
break;
case 'G':
dir = PACK_DIR_DOUBLE;
type = PACK_TYPE_FLOAT;
size = 8;
flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT;
break;
case 'g':
dir = PACK_DIR_FLOAT;
type = PACK_TYPE_FLOAT;
size = 4;
flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT;
break;
case 'H':
dir = PACK_DIR_HEX;
type = PACK_TYPE_STRING;
flags |= PACK_FLAG_COUNT2;
break;
case 'h':
dir = PACK_DIR_HEX;
type = PACK_TYPE_STRING;
flags |= PACK_FLAG_COUNT2 | PACK_FLAG_LSB;
break;
case 'I':
switch (sizeof(int)) {
case 2: t = 'S'; goto alias;
case 4: t = 'L'; goto alias;
case 8: t = 'Q'; goto alias;
default:
mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
}
break;
case 'i':
switch (sizeof(int)) {
case 2: t = 's'; goto alias;
case 4: t = 'l'; goto alias;
case 8: t = 'q'; goto alias;
default:
mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
}
break;
case 'L':
dir = PACK_DIR_LONG;
type = PACK_TYPE_INTEGER;
size = 4;
break;
case 'l':
dir = PACK_DIR_LONG;
type = PACK_TYPE_INTEGER;
size = 4;
flags |= PACK_FLAG_SIGNED;
break;
case 'm':
dir = PACK_DIR_BASE64;
type = PACK_TYPE_STRING;
flags |= PACK_FLAG_WIDTH;
break;
case 'N': /* = "L>" */
dir = PACK_DIR_LONG;
type = PACK_TYPE_INTEGER;
size = 4;
flags |= PACK_FLAG_GT;
break;
case 'n': /* = "S>" */
dir = PACK_DIR_SHORT;
type = PACK_TYPE_INTEGER;
size = 2;
flags |= PACK_FLAG_GT;
break;
case 'Q':
dir = PACK_DIR_QUAD;
type = PACK_TYPE_INTEGER;
size = 8;
break;
case 'q':
dir = PACK_DIR_QUAD;
type = PACK_TYPE_INTEGER;
size = 8;
flags |= PACK_FLAG_SIGNED;
break;
case 'S':
dir = PACK_DIR_SHORT;
type = PACK_TYPE_INTEGER;
size = 2;
break;
case 's':
dir = PACK_DIR_SHORT;
type = PACK_TYPE_INTEGER;
size = 2;
flags |= PACK_FLAG_SIGNED;
break;
case 'U':
dir = PACK_DIR_UTF8;
type = PACK_TYPE_INTEGER;
break;
case 'V': /* = "L<" */
dir = PACK_DIR_LONG;
type = PACK_TYPE_INTEGER;
size = 4;
flags |= PACK_FLAG_LT;
break;
case 'v': /* = "S<" */
dir = PACK_DIR_SHORT;
type = PACK_TYPE_INTEGER;
size = 2;
flags |= PACK_FLAG_LT;
break;
case 'x':
dir = PACK_DIR_NUL;
type = PACK_TYPE_NONE;
break;
case 'Z':
dir = PACK_DIR_STR;
type = PACK_TYPE_STRING;
flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_Z;
break;
default:
dir = PACK_DIR_INVALID;
type = PACK_TYPE_NONE;
break;
}
/* read suffix [0-9*_!<>] */
while (tmpl->idx < tlen) {
ch = tptr[tmpl->idx++];
if (isdigit(ch)) {
count = ch - '0';
while (tmpl->idx < tlen && isdigit(tptr[tmpl->idx])) {
count = count * 10 + (tptr[tmpl->idx++] - '0');
}
continue; /* special case */
} else if (ch == '*') {
count = -1;
} else if (ch == '_' || ch == '!' || ch == '<' || ch == '>') {
if (strchr("sSiIlLqQ", t) == NULL) {
char ch_str = ch;
mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%S' allowed only after types sSiIlLqQ", mrb_str_new(mrb, &ch_str, 1));
}
if (ch == '_' || ch == '!') {
flags |= PACK_FLAG_s;
} else if (ch == '<') {
flags |= PACK_FLAG_LT;
} else if (ch == '>') {
flags |= PACK_FLAG_GT;
}
} else {
tmpl->idx--;
break;
}
}
if ((flags & PACK_FLAG_LT) || (!(flags & PACK_FLAG_GT) && littleendian)) {
flags |= PACK_FLAG_LITTLEENDIAN;
}
*dirp = dir;
*typep = type;
*sizep = size;
*countp = count;
*flagsp = flags;
}
static mrb_value
mrb_pack_pack(mrb_state *mrb, mrb_value ary)
{
mrb_value o, result;
mrb_int aidx;
struct tmpl tmpl;
long count;
unsigned int flags;
int dir, ridx, size, type;
prepare_tmpl(mrb, &tmpl);
result = mrb_str_new(mrb, NULL, 128); /* allocate initial buffer */
aidx = 0;
ridx = 0;
while (has_tmpl(&tmpl)) {
read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags);
if (dir == PACK_DIR_INVALID)
continue;
else if (dir == PACK_DIR_NUL) {
ridx += pack_x(mrb, mrb_nil_value(), result, ridx, count, flags);
continue;
}
for (; aidx < RARRAY_LEN(ary); aidx++) {
if (count == 0 && !(flags & PACK_FLAG_WIDTH))
break;
o = mrb_ary_ref(mrb, ary, aidx);
if (type == PACK_TYPE_INTEGER) {
o = mrb_to_int(mrb, o);
} else if (type == PACK_TYPE_FLOAT) {
if (!mrb_float_p(o)) {
o = mrb_funcall(mrb, o, "to_f", 0);
}
} else if (type == PACK_TYPE_STRING) {
if (!mrb_string_p(o)) {
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
}
}
switch (dir) {
case PACK_DIR_CHAR:
ridx += pack_c(mrb, o, result, ridx, flags);
break;
case PACK_DIR_SHORT:
ridx += pack_s(mrb, o, result, ridx, flags);
break;
case PACK_DIR_LONG:
ridx += pack_l(mrb, o, result, ridx, flags);
break;
case PACK_DIR_QUAD:
ridx += pack_q(mrb, o, result, ridx, flags);
break;
case PACK_DIR_BASE64:
ridx += pack_m(mrb, o, result, ridx, count, flags);
break;
case PACK_DIR_HEX:
ridx += pack_h(mrb, o, result, ridx, count, flags);
break;
case PACK_DIR_STR:
ridx += pack_a(mrb, o, result, ridx, count, flags);
break;
case PACK_DIR_DOUBLE:
ridx += pack_double(mrb, o, result, ridx, flags);
break;
case PACK_DIR_FLOAT:
ridx += pack_float(mrb, o, result, ridx, flags);
break;
case PACK_DIR_UTF8:
ridx += pack_utf8(mrb, o, result, ridx, count, flags);
break;
default:
break;
}
if (dir == PACK_DIR_STR) { /* always consumes 1 entry */
aidx++;
break;
}
if (count > 0) {
count--;
}
}
}
mrb_str_resize(mrb, result, ridx);
return result;
}
static mrb_value
mrb_pack_unpack(mrb_state *mrb, mrb_value str)
{
mrb_value result;
struct tmpl tmpl;
long count;
unsigned int flags;
int dir, size, srcidx, srclen, type;
const unsigned char *sptr;
prepare_tmpl(mrb, &tmpl);
srcidx = 0;
srclen = RSTRING_LEN(str);
result = mrb_ary_new(mrb);
while (has_tmpl(&tmpl)) {
read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags);
if (dir == PACK_DIR_INVALID)
continue;
else if (dir == PACK_DIR_NUL) {
srcidx += unpack_x(mrb, sptr, srclen - srcidx, result, count, flags);
continue;
}
if (flags & PACK_FLAG_COUNT2) {
sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx;
switch (dir) {
case PACK_DIR_HEX:
srcidx += unpack_h(mrb, sptr, srclen - srcidx, result, count, flags);
break;
case PACK_DIR_STR:
srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags);
break;
}
continue;
}
while (count != 0) {
if (srclen - srcidx < size) {
while (count-- > 0) {
mrb_ary_push(mrb, result, mrb_nil_value());
}
break;
}
sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx;
switch (dir) {
case PACK_DIR_CHAR:
srcidx += unpack_c(mrb, sptr, srclen - srcidx, result, flags);
break;
case PACK_DIR_SHORT:
srcidx += unpack_s(mrb, sptr, srclen - srcidx, result, flags);
break;
case PACK_DIR_LONG:
srcidx += unpack_l(mrb, sptr, srclen - srcidx, result, flags);
break;
case PACK_DIR_QUAD:
srcidx += unpack_q(mrb, sptr, srclen - srcidx, result, flags);
break;
case PACK_DIR_BASE64:
srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
break;
case PACK_DIR_FLOAT:
srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags);
break;
case PACK_DIR_DOUBLE:
srcidx += unpack_double(mrb, sptr, srclen - srcidx, result, flags);
break;
case PACK_DIR_UTF8:
srcidx += unpack_utf8(mrb, sptr, srclen - srcidx, result, flags);
break;
default:
mrb_raise(mrb, E_RUNTIME_ERROR, "mruby-pack's bug");
}
if (count > 0) {
count--;
}
}
}
return result;
}
void
mrb_mruby_pack_gem_init(mrb_state *mrb)
{
littleendian = check_little_endian();
make_base64_dec_tab();
mrb_define_method(mrb, mrb->array_class, "pack", mrb_pack_pack, MRB_ARGS_REQ(1));
mrb_define_method(mrb, mrb->string_class, "unpack", mrb_pack_unpack, MRB_ARGS_REQ(1));
}
void
mrb_mruby_pack_gem_final(mrb_state *mrb)
{
}
# pack & unpack 'm' (base64)
assert('[""].pack("m")') do
ary = ""
str = ""
[ary].pack("m") == str and
str.unpack("m") == [ary]
end
assert('["\0"].pack("m")') do
ary = "\0"
str = "AA==\n"
[ary].pack("m") == str and
str.unpack("m") == [ary]
end
assert('["\0\0"].pack("m")') do
ary = "\0\0"
str = "AAA=\n"
[ary].pack("m") == str and
str.unpack("m") == [ary]
end
assert('["\0\0\0"].pack("m")') do
ary = "\0\0\0"
str = "AAAA\n"
[ary].pack("m") == str and
str.unpack("m") == [ary]
end
assert('["abc..xyzABC..XYZ"].pack("m")') do
["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"].pack("m") == "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n"
end
assert('"YWJ...".unpack("m") should "abc..xyzABC..XYZ"') do
str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJT\nVFVWV1hZWg==\n".unpack("m") == [str] and
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWg==\n".unpack("m") == [str]
end
# pack & unpack 'H'
assert('["3031"].pack("H*")') do
ary = "3031"
str = "01"
[ary].pack("H*") == str and
str.unpack("H*") == [ary]
end
assert('["10"].pack("H*")') do
ary = "10"
str = "\020"
[ary].pack("H*") == str and
str.unpack("H*") == [ary]
end
assert('[0,1,127,128,255].pack("C*")') do
ary = [ 0, 1, 127, 128, 255 ]
str = "\x00\x01\x7F\x80\xFF"
ary.pack("C*") == str and str.unpack("C*") == ary
end
# pack "a"
assert('["abc"].pack("a")') do
["abc"].pack("a") == "a" and
["abc"].pack("a*") == "abc" and
["abc"].pack("a4") == "abc\0"
end
# upack "a"
assert('["abc"].pack("a")') do
"abc\0".unpack("a4") == ["abc\0"] and
"abc ".unpack("a4") == ["abc "]
end
# pack "A"
assert('["abc"].pack("A")') do
["abc"].pack("A") == "a" and
["abc"].pack("A*") == "abc" and
["abc"].pack("A4") == "abc "
end
# upack "A"
assert('["abc"].pack("A")') do
"abc\0".unpack("A4") == ["abc"] and
"abc ".unpack("A4") == ["abc"]
end
# regression tests
assert('issue #1') do
[1, 2].pack("nn") == "\000\001\000\002"
end
def assert_pack tmpl, packed, unpacked
assert_equal packed, unpacked.pack(tmpl)
assert_equal unpacked, packed.unpack(tmpl)
end
PACK_IS_LITTLE_ENDIAN = "\x01\00".unpack('S')[0] == 0x01
assert 'pack float' do
assert_pack 'e', "\x00\x00@@", [3.0]
assert_pack 'g', "@@\x00\x00", [3.0]
if PACK_IS_LITTLE_ENDIAN
assert_pack 'f', "\x00\x00@@", [3.0]
assert_pack 'F', "\x00\x00@@", [3.0]
else
assert_pack 'f', "@@\x00\x00", [3.0]
assert_pack 'F', "@@\x00\x00", [3.0]
end
end
assert 'pack double' do
assert_pack 'E', "\x00\x00\x00\x00\x00\x00\b@", [3.0]
assert_pack 'G', "@\b\x00\x00\x00\x00\x00\x00", [3.0]
if PACK_IS_LITTLE_ENDIAN
assert_pack 'd', "\x00\x00\x00\x00\x00\x00\b@", [3.0]
assert_pack 'D', "\x00\x00\x00\x00\x00\x00\b@", [3.0]
else
assert_pack 'd', "@\b\x00\x00\x00\x00\x00\x00", [3.0]
assert_pack 'D', "@\b\x00\x00\x00\x00\x00\x00", [3.0]
end
end
assert 'pack/unpack "i"' do
int_size = [0].pack('i').size
raise "pack('i').size is too small (#{int_size})" if int_size < 2
if PACK_IS_LITTLE_ENDIAN
str = "\xC7\xCF" + "\xFF" * (int_size-2)
else
str = "\xFF" * (int_size-2) + "\xC7\xCF"
end
assert_pack 'i', str, [-12345]
end
assert 'pack/unpack "I"' do
uint_size = [0].pack('I').size
raise "pack('I').size is too small (#{uint_size})" if uint_size < 2
if PACK_IS_LITTLE_ENDIAN
str = "\x39\x30" + "\0" * (uint_size-2)
else
str = "\0" * (uint_size-2) + "\x39\x30"
end
assert_pack 'I', str, [12345]
end
assert 'pack/unpack "U"' do
assert_equal [], "".unpack("U")
assert_equal [], "".unpack("U*")
assert_equal [65, 66], "ABC".unpack("U2")
assert_equal [12371, 12435, 12395, 12385, 12399, 19990, 30028], "こんにちは世界".unpack("U*")
assert_equal "", [].pack("U")
assert_equal "", [].pack("U*")
assert_equal "AB", [65, 66, 67].pack("U2")
assert_equal "こんにちは世界", [12371, 12435, 12395, 12385, 12399, 19990, 30028].pack("U*")
assert_equal "\000", [0].pack("U")
assert_raise(RangeError) { [-0x40000000].pack("U") }
assert_raise(RangeError) { [-1].pack("U") }
assert_raise(RangeError) { [0x40000000].pack("U") }
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment