summaryrefslogtreecommitdiff
path: root/pack.c
diff options
context:
space:
mode:
Diffstat (limited to 'pack.c')
-rw-r--r--pack.c2710
1 files changed, 1187 insertions, 1523 deletions
diff --git a/pack.c b/pack.c
index 3305ccaa1e..2ef826b0e2 100644
--- a/pack.c
+++ b/pack.c
@@ -9,12 +9,22 @@
**********************************************************************/
-#include "ruby/encoding.h"
-#include "internal.h"
-#include <sys/types.h>
+#include "ruby/internal/config.h"
+
#include <ctype.h>
#include <errno.h>
#include <float.h>
+#include <sys/types.h>
+
+#include "internal.h"
+#include "internal/array.h"
+#include "internal/bits.h"
+#include "internal/string.h"
+#include "internal/symbol.h"
+#include "internal/variable.h"
+#include "ruby/util.h"
+
+#include "builtin.h"
/*
* It is intentional that the condition for natstr is HAVE_TRUE_LONG_LONG
@@ -26,10 +36,11 @@
*/
#ifdef HAVE_TRUE_LONG_LONG
static const char natstr[] = "sSiIlLqQjJ";
+# define endstr natstr
#else
static const char natstr[] = "sSiIlLjJ";
-#endif
static const char endstr[] = "sSiIlLqQjJ";
+#endif
#ifdef HAVE_TRUE_LONG_LONG
/* It is intentional to use long long instead of LONG_LONG. */
@@ -125,13 +136,29 @@ str_associate(VALUE str, VALUE add)
static VALUE
str_associated(VALUE str)
{
- return rb_ivar_lookup(str, id_associated, Qfalse);
+ VALUE associates = rb_ivar_lookup(str, id_associated, Qfalse);
+ if (!associates)
+ rb_raise(rb_eArgError, "no associated pointer");
+ return associates;
}
+static VALUE
+associated_pointer(VALUE associates, const char *t)
+{
+ const VALUE *p = RARRAY_CONST_PTR(associates);
+ const VALUE *pend = p + RARRAY_LEN(associates);
+ for (; p < pend; p++) {
+ VALUE tmp = *p;
+ if (RB_TYPE_P(tmp, T_STRING) && RSTRING_PTR(tmp) == t) return tmp;
+ }
+ rb_raise(rb_eArgError, "non associated pointer");
+ UNREACHABLE_RETURN(Qnil);
+}
+
+RBIMPL_ATTR_NORETURN()
static void
unknown_directive(const char *mode, char type, VALUE fmt)
{
- VALUE f;
char unknown[5];
if (ISPRINT(type)) {
@@ -141,12 +168,9 @@ unknown_directive(const char *mode, char type, VALUE fmt)
else {
snprintf(unknown, sizeof(unknown), "\\x%.2x", type & 0xff);
}
- f = rb_str_quote_unprintable(fmt);
- if (f != fmt) {
- fmt = rb_str_subseq(f, 1, RSTRING_LEN(f) - 2);
- }
- rb_warning("unknown %s directive '%s' in '%"PRIsVALUE"'",
- mode, unknown, fmt);
+ fmt = rb_str_quote_unprintable(fmt);
+ rb_raise(rb_eArgError, "unknown %s directive '%s' in '%"PRIsVALUE"'",
+ mode, unknown, fmt);
}
static float
@@ -169,171 +193,85 @@ VALUE_to_float(VALUE obj)
}
}
-/*
- * call-seq:
- * arr.pack( aTemplateString ) -> aBinaryString
- * arr.pack( aTemplateString, buffer: aBufferString ) -> aBufferString
- *
- * Packs the contents of <i>arr</i> into a binary sequence according to
- * the directives in <i>aTemplateString</i> (see the table below)
- * Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
- * which gives the width of the resulting field. The remaining
- * directives also may take a count, indicating the number of array
- * elements to convert. If the count is an asterisk
- * (``<code>*</code>''), all remaining array elements will be
- * converted. Any of the directives ``<code>sSiIlL</code>'' may be
- * followed by an underscore (``<code>_</code>'') or
- * exclamation mark (``<code>!</code>'') to use the underlying
- * platform's native size for the specified type; otherwise, they use a
- * platform-independent size. Spaces are ignored in the template
- * string. See also String#unpack.
- *
- * a = [ "a", "b", "c" ]
- * n = [ 65, 66, 67 ]
- * a.pack("A3A3A3") #=> "a b c "
- * 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.
- * When the offset is specified by the beginning of <i>aTemplateString</i>,
- * the result is filled after the offset.
- * If original contents of <i>aBufferString</i> exists and it's longer than
- * the offset, the rest of <i>offsetOfBuffer</i> are overwritten by the result.
- * 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 |
- * Directive | Element | Meaning
- * ----------------------------------------------------------------------------
- * C | Integer | 8-bit unsigned (unsigned char)
- * S | Integer | 16-bit unsigned, native endian (uint16_t)
- * L | Integer | 32-bit unsigned, native endian (uint32_t)
- * Q | Integer | 64-bit unsigned, native endian (uint64_t)
- * J | Integer | pointer width unsigned, native endian (uintptr_t)
- * | | (J is available since Ruby 2.3.)
- * | |
- * c | Integer | 8-bit signed (signed char)
- * s | Integer | 16-bit signed, native endian (int16_t)
- * l | Integer | 32-bit signed, native endian (int32_t)
- * q | Integer | 64-bit signed, native endian (int64_t)
- * j | Integer | pointer width signed, native endian (intptr_t)
- * | | (j is available since Ruby 2.3.)
- * | |
- * S_ S! | Integer | unsigned short, native endian
- * I I_ I! | Integer | unsigned int, native endian
- * L_ L! | Integer | unsigned long, native endian
- * Q_ Q! | Integer | unsigned long long, native endian (ArgumentError
- * | | if the platform has no long long type.)
- * | | (Q_ and Q! is available since Ruby 2.1.)
- * J! | Integer | uintptr_t, native endian (same with J)
- * | | (J! is available since Ruby 2.3.)
- * | |
- * s_ s! | Integer | signed short, native endian
- * i i_ i! | Integer | signed int, native endian
- * l_ l! | Integer | signed long, native endian
- * q_ q! | Integer | signed long long, native endian (ArgumentError
- * | | if the platform has no long long type.)
- * | | (q_ and q! is available since Ruby 2.1.)
- * j! | Integer | intptr_t, native endian (same with j)
- * | | (j! is available since Ruby 2.3.)
- * | |
- * S> s> S!> s!> | Integer | same as the directives without ">" except
- * L> l> L!> l!> | | big endian
- * I!> i!> | | (available since Ruby 1.9.3)
- * Q> q> Q!> q!> | | "S>" is same as "n"
- * J> j> J!> j!> | | "L>" is same as "N"
- * | |
- * S< s< S!< s!< | Integer | same as the directives without "<" except
- * L< l< L!< l!< | | little endian
- * I!< i!< | | (available since Ruby 1.9.3)
- * Q< q< Q!< q!< | | "S<" is same as "v"
- * J< j< J!< j!< | | "L<" is same as "V"
- * | |
- * 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
- * V | Integer | 32-bit unsigned, VAX (little-endian) byte order
- * | |
- * U | Integer | UTF-8 character
- * w | Integer | BER-compressed integer
- *
- * Float | Array |
- * Directive | Element | Meaning
- * ---------------------------------------------------------------------------
- * D d | Float | double-precision, native format
- * F f | Float | single-precision, native format
- * E | Float | double-precision, little-endian byte order
- * e | Float | single-precision, little-endian byte order
- * G | Float | double-precision, network (big-endian) byte order
- * g | Float | single-precision, network (big-endian) byte order
- *
- * String | Array |
- * Directive | Element | Meaning
- * ---------------------------------------------------------------------------
- * A | String | arbitrary binary string (space padded, count is width)
- * a | String | arbitrary binary string (null padded, count is width)
- * Z | String | same as ``a'', except that null is added with *
- * B | String | bit string (MSB first)
- * b | String | bit string (LSB first)
- * H | String | hex string (high nibble first)
- * h | String | hex string (low nibble first)
- * u | String | UU-encoded string
- * M | String | quoted printable, MIME encoding (see also RFC2045)
- * | | (text mode but input must use LF and output LF)
- * m | String | base64 encoded string (see RFC 2045, count is width)
- * | | (if count is 0, no line feed are added, see RFC 4648)
- * P | String | pointer to a structure (fixed-length string)
- * p | String | pointer to a null-terminated string
- *
- * Misc. | Array |
- * Directive | Element | Meaning
- * ---------------------------------------------------------------------------
- * @ | --- | moves to absolute position
- * X | --- | back up a byte
- * x | --- | null byte
- */
+static void
+str_expand_fill(VALUE res, int c, long len)
+{
+ long olen = RSTRING_LEN(res);
+ memset(RSTRING_PTR(res) + olen, c, len);
+ rb_str_set_len(res, olen + len);
+}
+
+static char *
+skip_to_eol(const char *p, const char *pend)
+{
+ p = memchr(p, '\n', pend - p);
+ return (char *)(p ? p + 1 : pend);
+}
+
+#define skip_blank(p, type) \
+ (ISSPACE(type) || (type == '#' && (p = skip_to_eol(p, pend), 1)))
+
+#ifndef NATINT_PACK
+# define pack_modifiers(p, t, n, e) pack_modifiers(p, t, e)
+#endif
+static char *
+pack_modifiers(const char *p, char type, int *natint, int *explicit_endian)
+{
+ while (1) {
+ switch (*p) {
+ case '_':
+ case '!':
+ if (strchr(natstr, type)) {
+#ifdef NATINT_PACK
+ *natint = 1;
+#endif
+ p++;
+ }
+ else {
+ rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
+ }
+ break;
+
+ 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++;
+ break;
+ default:
+ return (char *)p;
+ }
+ }
+}
static VALUE
-pack_pack(int argc, VALUE *argv, VALUE ary)
+pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
{
- static const char nul10[] = "\0\0\0\0\0\0\0\0\0\0";
- static const char spc10[] = " ";
const char *p, *pend;
- VALUE fmt, opt = Qnil, res, from, associates = 0, buffer = 0;
- char type;
+ VALUE res, from, associates = 0;
long len, idx, plen;
const char *ptr;
int enc_info = 1; /* 0 - BINARY, 1 - US-ASCII, 2 - UTF-8 */
-#ifdef NATINT_PACK
- int natint; /* native integer */
-#endif
int integer_size, bigendian_p;
- rb_scan_args(argc, argv, "10:", &fmt, &opt);
-
StringValue(fmt);
+ rb_must_asciicompat(fmt);
p = RSTRING_PTR(fmt);
pend = p + RSTRING_LEN(fmt);
- if (!NIL_P(opt)) {
- static ID keyword_ids[1];
- if (!keyword_ids[0])
- CONST_ID(keyword_ids[0], "buffer");
- rb_get_kwargs(opt, keyword_ids, 0, 1, &buffer);
-
- if (buffer != Qundef && !RB_TYPE_P(buffer, T_STRING))
- rb_raise(rb_eTypeError, "buffer must be String, not %s", rb_obj_classname(buffer));
+ if (NIL_P(buffer)) {
+ res = rb_str_buf_new(0);
+ }
+ else {
+ if (!RB_TYPE_P(buffer, T_STRING))
+ rb_raise(rb_eTypeError, "buffer must be String, not %s", rb_obj_classname(buffer));
+ rb_str_modify(buffer);
+ res = buffer;
}
- if (buffer)
- res = buffer;
- else
- res = rb_str_buf_new(0);
idx = 0;
@@ -343,330 +281,275 @@ pack_pack(int argc, VALUE *argv, VALUE ary)
#define NEXTFROM (MORE_ITEM ? RARRAY_AREF(ary, idx++) : TOO_FEW)
while (p < pend) {
- int explicit_endian = 0;
- if (RSTRING_PTR(fmt) + RSTRING_LEN(fmt) != pend) {
- rb_raise(rb_eRuntimeError, "format string modified");
- }
- type = *p++; /* get data type */
+ int explicit_endian = 0;
+ if (RSTRING_END(fmt) != pend) {
+ rb_raise(rb_eRuntimeError, "format string modified");
+ }
+ const char type = *p++; /* get data type */
#ifdef NATINT_PACK
- natint = 0;
+ int natint = 0; /* native integer */
#endif
- if (ISSPACE(type)) continue;
- if (type == '#') {
- while ((p < pend) && (*p != '\n')) {
- p++;
- }
- continue;
- }
-
- {
- modifiers:
- switch (*p) {
- case '_':
- case '!':
- if (strchr(natstr, type)) {
-#ifdef NATINT_PACK
- natint = 1;
-#endif
- 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
+ if (skip_blank(p, type)) continue;
+ p = pack_modifiers(p, type, &natint, &explicit_endian);
+
+ if (*p == '*') { /* set data length */
+ len = strchr("@Xxu", type) ? 0
: strchr("PMm", type) ? 1
: RARRAY_LEN(ary) - idx;
- p++;
- }
- else if (ISDIGIT(*p)) {
- errno = 0;
- len = STRTOUL(p, (char**)&p, 10);
- if (errno) {
- rb_raise(rb_eRangeError, "pack length too big");
- }
- }
- else {
- len = 1;
- }
-
- switch (type) {
- case 'U':
- /* if encoding is US-ASCII, upgrade to UTF-8 */
- if (enc_info == 1) enc_info = 2;
- break;
- case 'm': case 'M': case 'u':
- /* keep US-ASCII (do nothing) */
- break;
- default:
- /* fall back to BINARY */
- enc_info = 0;
- break;
- }
- switch (type) {
- case 'A': case 'a': case 'Z':
- case 'B': case 'b':
- case 'H': case 'h':
- from = NEXTFROM;
- if (NIL_P(from)) {
- ptr = "";
- plen = 0;
- }
- else {
- StringValue(from);
- ptr = RSTRING_PTR(from);
- plen = RSTRING_LEN(from);
- OBJ_INFECT(res, from);
- }
-
- if (p[-1] == '*')
- len = plen;
-
- switch (type) {
- case 'a': /* arbitrary binary string (null padded) */
- case 'A': /* arbitrary binary string (ASCII space padded) */
- case 'Z': /* null terminated string */
- if (plen >= len) {
- rb_str_buf_cat(res, ptr, len);
- if (p[-1] == '*' && type == 'Z')
- rb_str_buf_cat(res, nul10, 1);
- }
- else {
- rb_str_buf_cat(res, ptr, plen);
- len -= plen;
- while (len >= 10) {
- rb_str_buf_cat(res, (type == 'A')?spc10:nul10, 10);
- len -= 10;
- }
- rb_str_buf_cat(res, (type == 'A')?spc10:nul10, len);
- }
- break;
+ p++;
+ }
+ else if (ISDIGIT(*p)) {
+ errno = 0;
+ len = STRTOUL(p, (char**)&p, 10);
+ if (len < 0 || errno) {
+ rb_raise(rb_eRangeError, "pack length too big");
+ }
+ }
+ else {
+ len = 1;
+ }
+
+ switch (type) {
+ case 'U':
+ /* if encoding is US-ASCII, upgrade to UTF-8 */
+ if (enc_info == 1) enc_info = 2;
+ break;
+ case 'm': case 'M': case 'u':
+ /* keep US-ASCII (do nothing) */
+ break;
+ default:
+ /* fall back to BINARY */
+ enc_info = 0;
+ break;
+ }
+ switch (type) {
+ case 'A': case 'a': case 'Z':
+ case 'B': case 'b':
+ case 'H': case 'h':
+ from = NEXTFROM;
+ if (NIL_P(from)) {
+ ptr = "";
+ plen = 0;
+ }
+ else {
+ StringValue(from);
+ ptr = RSTRING_PTR(from);
+ plen = RSTRING_LEN(from);
+ }
+
+ if (p[-1] == '*')
+ len = plen;
+
+ switch (type) {
+ case 'a': /* arbitrary binary string (null padded) */
+ case 'A': /* arbitrary binary string (ASCII space padded) */
+ case 'Z': /* null terminated string */
+ if (plen >= len) {
+ rb_str_buf_cat(res, ptr, len);
+ if (p[-1] == '*' && type == 'Z')
+ rb_str_buf_cat(res, "", 1);
+ }
+ else {
+ rb_str_modify_expand(res, len);
+ rb_str_buf_cat(res, ptr, plen);
+ str_expand_fill(res, (type == 'A' ? ' ' : '\0'), len - plen);
+ }
+ break;
#define castchar(from) (char)((from) & 0xff)
- case 'b': /* bit string (ascending) */
- {
- int byte = 0;
- long i, j = 0;
-
- if (len > plen) {
- j = (len - plen + 1)/2;
- len = plen;
- }
- for (i=0; i++ < len; ptr++) {
- if (*ptr & 1)
- byte |= 128;
- if (i & 7)
- byte >>= 1;
- else {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- byte = 0;
- }
- }
- if (len & 7) {
- char c;
- byte >>= 7 - (len & 7);
- c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- }
- len = j;
- goto grow;
- }
- break;
-
- case 'B': /* bit string (descending) */
- {
- int byte = 0;
- long i, j = 0;
-
- if (len > plen) {
- j = (len - plen + 1)/2;
- len = plen;
- }
- for (i=0; i++ < len; ptr++) {
- byte |= *ptr & 1;
- if (i & 7)
- byte <<= 1;
- else {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- byte = 0;
- }
- }
- if (len & 7) {
- char c;
- byte <<= 7 - (len & 7);
- c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- }
- len = j;
- goto grow;
- }
- break;
-
- case 'h': /* hex string (low nibble first) */
- {
- int byte = 0;
- long i, j = 0;
-
- if (len > plen) {
- j = (len + 1) / 2 - (plen + 1) / 2;
- len = plen;
- }
- for (i=0; i++ < len; ptr++) {
- if (ISALPHA(*ptr))
- byte |= (((*ptr & 15) + 9) & 15) << 4;
- else
- byte |= (*ptr & 15) << 4;
- if (i & 1)
- byte >>= 4;
- else {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- byte = 0;
- }
- }
- if (len & 1) {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- }
- len = j;
- goto grow;
- }
- break;
-
- case 'H': /* hex string (high nibble first) */
- {
- int byte = 0;
- long i, j = 0;
-
- if (len > plen) {
- j = (len + 1) / 2 - (plen + 1) / 2;
- len = plen;
- }
- for (i=0; i++ < len; ptr++) {
- if (ISALPHA(*ptr))
- byte |= ((*ptr & 15) + 9) & 15;
- else
- byte |= *ptr & 15;
- if (i & 1)
- byte <<= 4;
- else {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- byte = 0;
- }
- }
- if (len & 1) {
- char c = castchar(byte);
- rb_str_buf_cat(res, &c, 1);
- }
- len = j;
- goto grow;
- }
- break;
- }
- break;
-
- case 'c': /* signed char */
- case 'C': /* unsigned char */
+ case 'b': /* bit string (ascending) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len - plen + 1)/2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (*ptr & 1)
+ byte |= 128;
+ if (i & 7)
+ byte >>= 1;
+ else {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 7) {
+ char c;
+ byte >>= 7 - (len & 7);
+ c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'B': /* bit string (descending) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len - plen + 1)/2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ byte |= *ptr & 1;
+ if (i & 7)
+ byte <<= 1;
+ else {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 7) {
+ char c;
+ byte <<= 7 - (len & 7);
+ c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'h': /* hex string (low nibble first) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len + 1) / 2 - (plen + 1) / 2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (ISALPHA(*ptr))
+ byte |= (((*ptr & 15) + 9) & 15) << 4;
+ else
+ byte |= (*ptr & 15) << 4;
+ if (i & 1)
+ byte >>= 4;
+ else {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 1) {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+
+ case 'H': /* hex string (high nibble first) */
+ {
+ int byte = 0;
+ long i, j = 0;
+
+ if (len > plen) {
+ j = (len + 1) / 2 - (plen + 1) / 2;
+ len = plen;
+ }
+ for (i=0; i++ < len; ptr++) {
+ if (ISALPHA(*ptr))
+ byte |= ((*ptr & 15) + 9) & 15;
+ else
+ byte |= *ptr & 15;
+ if (i & 1)
+ byte <<= 4;
+ else {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ byte = 0;
+ }
+ }
+ if (len & 1) {
+ char c = castchar(byte);
+ rb_str_buf_cat(res, &c, 1);
+ }
+ len = j;
+ goto grow;
+ }
+ break;
+ }
+ break;
+
+ case 'c': /* signed char */
+ case 'C': /* unsigned char */
integer_size = 1;
bigendian_p = BIGENDIAN_P(); /* not effective */
goto pack_integer;
- case 's': /* s for int16_t, s! for signed short */
- integer_size = NATINT_LEN(short, 2);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
- case 'S': /* S for uint16_t, S! for unsigned short */
+ case 's': /* s for int16_t, s! for signed short */
+ case 'S': /* S for uint16_t, S! for unsigned short */
integer_size = NATINT_LEN(short, 2);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'i': /* i and i! for signed int */
+ case 'i': /* i and i! for signed int */
+ case 'I': /* I and I! for unsigned int */
integer_size = (int)sizeof(int);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'I': /* I and I! for unsigned int */
- integer_size = (int)sizeof(int);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
- case 'l': /* l for int32_t, l! for signed long */
+ case 'l': /* l for int32_t, l! for signed long */
+ case 'L': /* L for uint32_t, L! for unsigned long */
integer_size = NATINT_LEN(long, 4);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'L': /* L for uint32_t, L! for unsigned long */
- integer_size = NATINT_LEN(long, 4);
+ case 'q': /* q for int64_t, q! for signed long long */
+ case 'Q': /* Q for uint64_t, Q! for unsigned long long */
+ integer_size = NATINT_LEN_Q;
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'q': /* q for int64_t, q! for signed long long */
- integer_size = NATINT_LEN_Q;
+ case 'j': /* j for intptr_t */
+ integer_size = sizeof(intptr_t);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'Q': /* Q for uint64_t, Q! for unsigned long long */
- integer_size = NATINT_LEN_Q;
+ case 'J': /* J for uintptr_t */
+ integer_size = sizeof(uintptr_t);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
- case 'j': /* j for intptr_t */
- integer_size = sizeof(intptr_t);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
- case 'J': /* J for uintptr_t */
- integer_size = sizeof(uintptr_t);
- bigendian_p = BIGENDIAN_P();
- goto pack_integer;
-
- case 'n': /* 16 bit (2 bytes) integer (network byte-order) */
+ case 'n': /* 16 bit (2 bytes) integer (network byte-order) */
integer_size = 2;
bigendian_p = 1;
goto pack_integer;
- case 'N': /* 32 bit (4 bytes) integer (network byte-order) */
+ case 'N': /* 32 bit (4 bytes) integer (network byte-order) */
integer_size = 4;
bigendian_p = 1;
goto pack_integer;
- case 'v': /* 16 bit (2 bytes) integer (VAX byte-order) */
+ case 'v': /* 16 bit (2 bytes) integer (VAX byte-order) */
integer_size = 2;
bigendian_p = 0;
goto pack_integer;
- case 'V': /* 32 bit (4 bytes) integer (VAX byte-order) */
+ case 'V': /* 32 bit (4 bytes) integer (VAX byte-order) */
integer_size = 4;
bigendian_p = 0;
goto pack_integer;
pack_integer:
- if (explicit_endian) {
- bigendian_p = explicit_endian == '>';
- }
+ if (explicit_endian) {
+ bigendian_p = explicit_endian == '>';
+ }
if (integer_size > MAX_INTEGER_PACK_SIZE)
- rb_bug("unexpected intger size for pack: %d", integer_size);
+ rb_bug("unexpected integer size for pack: %d", integer_size);
while (len-- > 0) {
char intbuf[MAX_INTEGER_PACK_SIZE];
@@ -676,196 +559,189 @@ pack_pack(int argc, VALUE *argv, VALUE ary)
(bigendian_p ? INTEGER_PACK_BIG_ENDIAN : INTEGER_PACK_LITTLE_ENDIAN));
rb_str_buf_cat(res, intbuf, integer_size);
}
- break;
+ break;
- case 'f': /* single precision float in native format */
- case 'F': /* ditto */
- while (len-- > 0) {
- float f;
+ case 'f': /* single precision float in native format */
+ case 'F': /* ditto */
+ while (len-- > 0) {
+ float f;
- from = NEXTFROM;
+ from = NEXTFROM;
f = VALUE_to_float(from);
- rb_str_buf_cat(res, (char*)&f, sizeof(float));
- }
- break;
+ rb_str_buf_cat(res, (char*)&f, sizeof(float));
+ }
+ break;
- case 'e': /* single precision float in VAX byte-order */
- while (len-- > 0) {
- FLOAT_CONVWITH(tmp);
+ case 'e': /* single precision float in VAX byte-order */
+ while (len-- > 0) {
+ FLOAT_CONVWITH(tmp);
- from = NEXTFROM;
+ from = NEXTFROM;
tmp.f = VALUE_to_float(from);
- HTOVF(tmp);
- rb_str_buf_cat(res, tmp.buf, sizeof(float));
- }
- break;
-
- case 'E': /* double precision float in VAX byte-order */
- while (len-- > 0) {
- DOUBLE_CONVWITH(tmp);
- from = NEXTFROM;
- tmp.d = RFLOAT_VALUE(rb_to_float(from));
- HTOVD(tmp);
- rb_str_buf_cat(res, tmp.buf, sizeof(double));
- }
- break;
-
- case 'd': /* double precision float in native format */
- case 'D': /* ditto */
- while (len-- > 0) {
- double d;
-
- from = NEXTFROM;
- d = RFLOAT_VALUE(rb_to_float(from));
- rb_str_buf_cat(res, (char*)&d, sizeof(double));
- }
- break;
-
- case 'g': /* single precision float in network byte-order */
- while (len-- > 0) {
- FLOAT_CONVWITH(tmp);
- from = NEXTFROM;
+ HTOVF(tmp);
+ rb_str_buf_cat(res, tmp.buf, sizeof(float));
+ }
+ break;
+
+ case 'E': /* double precision float in VAX byte-order */
+ while (len-- > 0) {
+ DOUBLE_CONVWITH(tmp);
+ from = NEXTFROM;
+ tmp.d = RFLOAT_VALUE(rb_to_float(from));
+ HTOVD(tmp);
+ rb_str_buf_cat(res, tmp.buf, sizeof(double));
+ }
+ break;
+
+ case 'd': /* double precision float in native format */
+ case 'D': /* ditto */
+ while (len-- > 0) {
+ double d;
+
+ from = NEXTFROM;
+ d = RFLOAT_VALUE(rb_to_float(from));
+ rb_str_buf_cat(res, (char*)&d, sizeof(double));
+ }
+ break;
+
+ case 'g': /* single precision float in network byte-order */
+ while (len-- > 0) {
+ FLOAT_CONVWITH(tmp);
+ from = NEXTFROM;
tmp.f = VALUE_to_float(from);
- HTONF(tmp);
- rb_str_buf_cat(res, tmp.buf, sizeof(float));
- }
- break;
-
- case 'G': /* double precision float in network byte-order */
- while (len-- > 0) {
- DOUBLE_CONVWITH(tmp);
-
- from = NEXTFROM;
- tmp.d = RFLOAT_VALUE(rb_to_float(from));
- HTOND(tmp);
- rb_str_buf_cat(res, tmp.buf, sizeof(double));
- }
- break;
-
- case 'x': /* null byte */
- grow:
- while (len >= 10) {
- rb_str_buf_cat(res, nul10, 10);
- len -= 10;
- }
- rb_str_buf_cat(res, nul10, len);
- break;
-
- case 'X': /* back up byte */
- shrink:
- plen = RSTRING_LEN(res);
- if (plen < len)
- rb_raise(rb_eArgError, "X outside of string");
- rb_str_set_len(res, plen - len);
- break;
-
- case '@': /* null fill to absolute position */
- len -= RSTRING_LEN(res);
- if (len > 0) goto grow;
- len = -len;
- if (len > 0) goto shrink;
- break;
-
- case '%':
- rb_raise(rb_eArgError, "%% is not supported");
- break;
-
- case 'U': /* Unicode character */
- while (len-- > 0) {
- SIGNED_VALUE l;
- char buf[8];
- int le;
-
- from = NEXTFROM;
- from = rb_to_int(from);
- l = NUM2LONG(from);
- if (l < 0) {
- rb_raise(rb_eRangeError, "pack(U): value out of range");
- }
- le = rb_uv_to_utf8(buf, l);
- rb_str_buf_cat(res, (char*)buf, le);
- }
- break;
-
- case 'u': /* uuencoded string */
- case 'm': /* base64 encoded string */
- from = NEXTFROM;
- StringValue(from);
- ptr = RSTRING_PTR(from);
- plen = RSTRING_LEN(from);
- OBJ_INFECT(res, from);
-
- if (len == 0 && type == 'm') {
- encodes(res, ptr, plen, type, 0);
- ptr += plen;
- break;
- }
- if (len <= 2)
- len = 45;
- else if (len > 63 && type == 'u')
- len = 63;
- else
- len = len / 3 * 3;
- while (plen > 0) {
- long todo;
-
- if (plen > len)
- todo = len;
- else
- todo = plen;
- encodes(res, ptr, todo, type, 1);
- plen -= todo;
- ptr += todo;
- }
- break;
-
- case 'M': /* quoted-printable encoded string */
- from = rb_obj_as_string(NEXTFROM);
- OBJ_INFECT(res, from);
- if (len <= 1)
- len = 72;
- qpencode(res, from, len);
- break;
-
- case 'P': /* pointer to packed byte string */
- from = THISFROM;
- if (!NIL_P(from)) {
- StringValue(from);
- if (RSTRING_LEN(from) < len) {
- rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
- RSTRING_LEN(from), len);
- }
- }
- len = 1;
- /* FALL THROUGH */
- case 'p': /* pointer to string */
- while (len-- > 0) {
- char *t;
- from = NEXTFROM;
- if (NIL_P(from)) {
- t = 0;
- }
- else {
- t = StringValuePtr(from);
- OBJ_INFECT(res, from);
- rb_obj_taint(from);
- }
- if (!associates) {
- associates = rb_ary_new();
- }
- rb_ary_push(associates, from);
- rb_str_buf_cat(res, (char*)&t, sizeof(char*));
- }
- break;
-
- case 'w': /* BER compressed integer */
- while (len-- > 0) {
- VALUE buf = rb_str_new(0, 0);
+ HTONF(tmp);
+ rb_str_buf_cat(res, tmp.buf, sizeof(float));
+ }
+ break;
+
+ case 'G': /* double precision float in network byte-order */
+ while (len-- > 0) {
+ DOUBLE_CONVWITH(tmp);
+
+ from = NEXTFROM;
+ tmp.d = RFLOAT_VALUE(rb_to_float(from));
+ HTOND(tmp);
+ rb_str_buf_cat(res, tmp.buf, sizeof(double));
+ }
+ break;
+
+ case 'x': /* null byte */
+ grow:
+ rb_str_modify_expand(res, len);
+ str_expand_fill(res, '\0', len);
+ break;
+
+ case 'X': /* back up byte */
+ shrink:
+ plen = RSTRING_LEN(res);
+ if (plen < len)
+ rb_raise(rb_eArgError, "X outside of string");
+ rb_str_set_len(res, plen - len);
+ break;
+
+ case '@': /* null fill to absolute position */
+ len -= RSTRING_LEN(res);
+ if (len > 0) goto grow;
+ len = -len;
+ if (len > 0) goto shrink;
+ break;
+
+ case '%':
+ rb_raise(rb_eArgError, "%% is not supported");
+ break;
+
+ case 'U': /* Unicode character */
+ while (len-- > 0) {
+ SIGNED_VALUE l;
+ char buf[8];
+ int le;
+
+ from = NEXTFROM;
+ from = rb_to_int(from);
+ l = NUM2LONG(from);
+ if (l < 0) {
+ rb_raise(rb_eRangeError, "pack(U): value out of range");
+ }
+ le = rb_uv_to_utf8(buf, l);
+ rb_str_buf_cat(res, (char*)buf, le);
+ }
+ break;
+
+ case 'u': /* uuencoded string */
+ case 'm': /* base64 encoded string */
+ from = NEXTFROM;
+ StringValue(from);
+ ptr = RSTRING_PTR(from);
+ plen = RSTRING_LEN(from);
+
+ if (len == 0 && type == 'm') {
+ encodes(res, ptr, plen, type, 0);
+ ptr += plen;
+ break;
+ }
+ if (len <= 2)
+ len = 45;
+ else if (len > 63 && type == 'u')
+ len = 63;
+ else
+ len = len / 3 * 3;
+ while (plen > 0) {
+ long todo;
+
+ if (plen > len)
+ todo = len;
+ else
+ todo = plen;
+ encodes(res, ptr, todo, type, 1);
+ plen -= todo;
+ ptr += todo;
+ }
+ break;
+
+ case 'M': /* quoted-printable encoded string */
+ from = rb_obj_as_string(NEXTFROM);
+ if (len <= 1)
+ len = 72;
+ qpencode(res, from, len);
+ break;
+
+ case 'P': /* pointer to packed byte string */
+ from = THISFROM;
+ if (!NIL_P(from)) {
+ StringValue(from);
+ if (RSTRING_LEN(from) < len) {
+ rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
+ RSTRING_LEN(from), len);
+ }
+ }
+ len = 1;
+ /* FALL THROUGH */
+ case 'p': /* pointer to string */
+ while (len-- > 0) {
+ char *t;
+ from = NEXTFROM;
+ if (NIL_P(from)) {
+ t = 0;
+ }
+ else {
+ t = StringValuePtr(from);
+ }
+ if (!associates) {
+ associates = rb_ary_new();
+ }
+ rb_ary_push(associates, from);
+ rb_str_buf_cat(res, (char*)&t, sizeof(char*));
+ }
+ break;
+
+ case 'w': /* BER compressed integer */
+ while (len-- > 0) {
+ VALUE buf;
size_t numbytes;
int sign;
char *cp;
- from = NEXTFROM;
+ from = NEXTFROM;
from = rb_to_int(from);
numbytes = rb_absint_numwords(from, 7, NULL);
if (numbytes == 0)
@@ -887,34 +763,39 @@ pack_pack(int argc, VALUE *argv, VALUE ary)
}
rb_str_buf_cat(res, RSTRING_PTR(buf), RSTRING_LEN(buf));
- }
- break;
+ }
+ break;
- default: {
+ default: {
unknown_directive("pack", type, fmt);
- break;
- }
- }
+ break;
+ }
+ }
}
if (associates) {
- str_associate(res, associates);
+ str_associate(res, associates);
}
- OBJ_INFECT(res, fmt);
switch (enc_info) {
case 1:
- ENCODING_CODERANGE_SET(res, rb_usascii_encindex(), ENC_CODERANGE_7BIT);
- break;
+ ENCODING_CODERANGE_SET(res, rb_usascii_encindex(), ENC_CODERANGE_7BIT);
+ break;
case 2:
- rb_enc_set_index(res, rb_utf8_encindex());
- break;
+ rb_enc_set_index(res, rb_utf8_encindex());
+ break;
default:
- /* do nothing, keep ASCII-8BIT */
- break;
+ /* do nothing, keep ASCII-8BIT */
+ break;
}
return res;
}
+VALUE
+rb_ec_pack_ary(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
+{
+ return pack_pack(ec, ary, fmt, buffer);
+}
+
static const char uu_table[] =
"`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
static const char b64_table[] =
@@ -931,11 +812,11 @@ encodes(VALUE str, const char *s0, long len, int type, int tail_lf)
const unsigned char *s = (const unsigned char *)s0;
if (type == 'u') {
- buff[i++] = (char)len + ' ';
- padding = '`';
+ buff[i++] = (char)len + ' ';
+ padding = '`';
}
else {
- padding = '=';
+ padding = '=';
}
while (len >= input_unit) {
while (len >= input_unit && buff_size-i >= encoded_unit) {
@@ -953,16 +834,16 @@ encodes(VALUE str, const char *s0, long len, int type, int tail_lf)
}
if (len == 2) {
- buff[i++] = trans[077 & (*s >> 2)];
- buff[i++] = trans[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
- buff[i++] = trans[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
- buff[i++] = padding;
+ buff[i++] = trans[077 & (*s >> 2)];
+ buff[i++] = trans[077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))];
+ buff[i++] = trans[077 & (((s[1] << 2) & 074) | (('\0' >> 6) & 03))];
+ buff[i++] = padding;
}
else if (len == 1) {
- buff[i++] = trans[077 & (*s >> 2)];
- buff[i++] = trans[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
- buff[i++] = padding;
- buff[i++] = padding;
+ buff[i++] = trans[077 & (*s >> 2)];
+ buff[i++] = trans[077 & (((*s << 4) & 060) | (('\0' >> 4) & 017))];
+ buff[i++] = padding;
+ buff[i++] = padding;
}
if (tail_lf) buff[i++] = '\n';
rb_str_buf_cat(str, buff, i);
@@ -981,46 +862,46 @@ qpencode(VALUE str, VALUE from, long len)
while (s < send) {
if ((*s > 126) ||
- (*s < 32 && *s != '\n' && *s != '\t') ||
- (*s == '=')) {
- buff[i++] = '=';
- buff[i++] = hex_table[*s >> 4];
- buff[i++] = hex_table[*s & 0x0f];
+ (*s < 32 && *s != '\n' && *s != '\t') ||
+ (*s == '=')) {
+ buff[i++] = '=';
+ buff[i++] = hex_table[*s >> 4];
+ buff[i++] = hex_table[*s & 0x0f];
n += 3;
prev = EOF;
}
- else if (*s == '\n') {
+ else if (*s == '\n') {
if (prev == ' ' || prev == '\t') {
- buff[i++] = '=';
- buff[i++] = *s;
+ buff[i++] = '=';
+ buff[i++] = *s;
}
- buff[i++] = *s;
+ buff[i++] = *s;
n = 0;
prev = *s;
}
- else {
- buff[i++] = *s;
+ else {
+ buff[i++] = *s;
n++;
prev = *s;
}
if (n > len) {
- buff[i++] = '=';
- buff[i++] = '\n';
+ buff[i++] = '=';
+ buff[i++] = '\n';
n = 0;
prev = '\n';
}
- if (i > 1024 - 5) {
- rb_str_buf_cat(str, buff, i);
- i = 0;
- }
- s++;
+ if (i > 1024 - 5) {
+ rb_str_buf_cat(str, buff, i);
+ i = 0;
+ }
+ s++;
}
if (n > 0) {
- buff[i++] = '=';
- buff[i++] = '\n';
+ buff[i++] = '=';
+ buff[i++] = '\n';
}
if (i > 0) {
- rb_str_buf_cat(str, buff, i);
+ rb_str_buf_cat(str, buff, i);
}
}
@@ -1038,15 +919,15 @@ hex2num(char c)
tmp_len = 0; \
if (len > (long)((send-s)/(sz))) { \
if (!star) { \
- tmp_len = len-(send-s)/(sz); \
+ tmp_len = len-(send-s)/(sz); \
} \
- len = (send-s)/(sz); \
+ len = (send-s)/(sz); \
} \
} while (0)
#define PACK_ITEM_ADJUST() do { \
if (tmp_len > 0 && mode == UNPACK_ARRAY) \
- rb_ary_store(ary, RARRAY_LEN(ary)+tmp_len-1, Qnil); \
+ rb_ary_store(ary, RARRAY_LEN(ary)+tmp_len-1, Qnil); \
} while (0)
/* Workaround for Oracle Developer Studio (Oracle Solaris Studio)
@@ -1059,350 +940,308 @@ hex2num(char c)
# define AVOID_CC_BUG
#endif
-static VALUE
-infected_str_new(const char *ptr, long len, VALUE str)
-{
- VALUE s = rb_str_new(ptr, len);
-
- OBJ_INFECT(s, str);
- return s;
-}
-
-/* unpack mode */
-#define UNPACK_ARRAY 0
-#define UNPACK_BLOCK 1
-#define UNPACK_1 2
+enum unpack_mode {
+ UNPACK_ARRAY,
+ UNPACK_BLOCK,
+ UNPACK_1
+};
static VALUE
-pack_unpack_internal(VALUE str, VALUE fmt, int mode)
+pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset)
{
#define hexdigits ruby_hexdigits
char *s, *send;
char *p, *pend;
- VALUE ary;
- char type;
+ VALUE ary, associates = Qfalse;
long len;
AVOID_CC_BUG long tmp_len;
- int star;
-#ifdef NATINT_PACK
- int natint; /* native integer */
-#endif
int signed_p, integer_size, bigendian_p;
#define UNPACK_PUSH(item) do {\
- VALUE item_val = (item);\
- if ((mode) == UNPACK_BLOCK) {\
- rb_yield(item_val);\
- }\
- else if ((mode) == UNPACK_ARRAY) {\
- rb_ary_push(ary, item_val);\
- }\
- else /* if ((mode) == UNPACK_1) { */ {\
- return item_val; \
- }\
+ VALUE item_val = (item);\
+ if ((mode) == UNPACK_BLOCK) {\
+ rb_yield(item_val);\
+ }\
+ else if ((mode) == UNPACK_ARRAY) {\
+ rb_ary_push(ary, item_val);\
+ }\
+ else /* if ((mode) == UNPACK_1) { */ {\
+ return item_val; \
+ }\
} while (0)
StringValue(str);
StringValue(fmt);
+ rb_must_asciicompat(fmt);
+
+ if (offset < 0) rb_raise(rb_eArgError, "offset can't be negative");
+ len = RSTRING_LEN(str);
+ if (offset > len) rb_raise(rb_eArgError, "offset outside of string");
+
s = RSTRING_PTR(str);
- send = s + RSTRING_LEN(str);
+ send = s + len;
+ s += offset;
+
p = RSTRING_PTR(fmt);
pend = p + RSTRING_LEN(fmt);
+#define UNPACK_FETCH(var, type) (memcpy((var), s, sizeof(type)), s += sizeof(type))
+
ary = mode == UNPACK_ARRAY ? rb_ary_new() : Qnil;
while (p < pend) {
- int explicit_endian = 0;
- type = *p++;
+ int explicit_endian = 0;
+ const char type = *p++;
#ifdef NATINT_PACK
- natint = 0;
+ int natint = 0; /* native integer */
#endif
+ int star = 0;
- if (ISSPACE(type)) continue;
- if (type == '#') {
- while ((p < pend) && (*p != '\n')) {
- p++;
- }
- continue;
- }
-
- star = 0;
- {
- modifiers:
- switch (*p) {
- case '_':
- case '!':
-
- if (strchr(natstr, type)) {
-#ifdef NATINT_PACK
- natint = 1;
-#endif
- 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 == '*') {
- star = 1;
- len = send - s;
- p++;
- }
- else if (ISDIGIT(*p)) {
- errno = 0;
- len = STRTOUL(p, (char**)&p, 10);
- if (len < 0 || errno) {
- rb_raise(rb_eRangeError, "pack length too big");
- }
- }
- else {
- len = (type != '@');
- }
-
- switch (type) {
- case '%':
- rb_raise(rb_eArgError, "%% is not supported");
- break;
-
- case 'A':
- if (len > send - s) len = send - s;
- {
- long end = len;
- char *t = s + len - 1;
-
- while (t >= s) {
- if (*t != ' ' && *t != '\0') break;
- t--; len--;
- }
- UNPACK_PUSH(infected_str_new(s, len, str));
- s += end;
- }
- break;
-
- case 'Z':
- {
- char *t = s;
-
- if (len > send-s) len = send-s;
- while (t < s+len && *t) t++;
- UNPACK_PUSH(infected_str_new(s, t-s, str));
- if (t < send) t++;
- s = star ? t : s+len;
- }
- break;
-
- case 'a':
- if (len > send - s) len = send - s;
- UNPACK_PUSH(infected_str_new(s, len, str));
- s += len;
- break;
-
- case 'b':
- {
- VALUE bitstr;
- char *t;
- int bits;
- long i;
-
- if (p[-1] == '*' || len > (send - s) * 8)
- len = (send - s) * 8;
- bits = 0;
- bitstr = rb_usascii_str_new(0, len);
- OBJ_INFECT(bitstr, str);
- t = RSTRING_PTR(bitstr);
- for (i=0; i<len; i++) {
- if (i & 7) bits >>= 1;
- else bits = (unsigned char)*s++;
- *t++ = (bits & 1) ? '1' : '0';
- }
- UNPACK_PUSH(bitstr);
- }
- break;
-
- case 'B':
- {
- VALUE bitstr;
- char *t;
- int bits;
- long i;
-
- if (p[-1] == '*' || len > (send - s) * 8)
- len = (send - s) * 8;
- bits = 0;
- bitstr = rb_usascii_str_new(0, len);
- OBJ_INFECT(bitstr, str);
- t = RSTRING_PTR(bitstr);
- for (i=0; i<len; i++) {
- if (i & 7) bits <<= 1;
- else bits = (unsigned char)*s++;
- *t++ = (bits & 128) ? '1' : '0';
- }
- UNPACK_PUSH(bitstr);
- }
- break;
-
- case 'h':
- {
- VALUE bitstr;
- char *t;
- int bits;
- long i;
-
- if (p[-1] == '*' || len > (send - s) * 2)
- len = (send - s) * 2;
- bits = 0;
- bitstr = rb_usascii_str_new(0, len);
- OBJ_INFECT(bitstr, str);
- t = RSTRING_PTR(bitstr);
- for (i=0; i<len; i++) {
- if (i & 1)
- bits >>= 4;
- else
- bits = (unsigned char)*s++;
- *t++ = hexdigits[bits & 15];
- }
- UNPACK_PUSH(bitstr);
- }
- break;
-
- case 'H':
- {
- VALUE bitstr;
- char *t;
- int bits;
- long i;
-
- if (p[-1] == '*' || len > (send - s) * 2)
- len = (send - s) * 2;
- bits = 0;
- bitstr = rb_usascii_str_new(0, len);
- OBJ_INFECT(bitstr, str);
- t = RSTRING_PTR(bitstr);
- for (i=0; i<len; i++) {
- if (i & 1)
- bits <<= 4;
- else
- bits = (unsigned char)*s++;
- *t++ = hexdigits[(bits >> 4) & 15];
- }
- UNPACK_PUSH(bitstr);
- }
- break;
-
- case 'c':
- signed_p = 1;
- integer_size = 1;
- bigendian_p = BIGENDIAN_P(); /* not effective */
- goto unpack_integer;
-
- case 'C':
- signed_p = 0;
- integer_size = 1;
- bigendian_p = BIGENDIAN_P(); /* not effective */
- goto unpack_integer;
-
- case 's':
- signed_p = 1;
- integer_size = NATINT_LEN(short, 2);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'S':
- signed_p = 0;
- integer_size = NATINT_LEN(short, 2);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'i':
- signed_p = 1;
- integer_size = (int)sizeof(int);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'I':
- signed_p = 0;
- integer_size = (int)sizeof(int);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'l':
- signed_p = 1;
- integer_size = NATINT_LEN(long, 4);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'L':
- signed_p = 0;
- integer_size = NATINT_LEN(long, 4);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'q':
- signed_p = 1;
- integer_size = NATINT_LEN_Q;
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'Q':
- signed_p = 0;
- integer_size = NATINT_LEN_Q;
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'j':
- signed_p = 1;
- integer_size = sizeof(intptr_t);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'J':
- signed_p = 0;
- integer_size = sizeof(uintptr_t);
- bigendian_p = BIGENDIAN_P();
- goto unpack_integer;
-
- case 'n':
- signed_p = 0;
- integer_size = 2;
- bigendian_p = 1;
- goto unpack_integer;
-
- case 'N':
- signed_p = 0;
- integer_size = 4;
- bigendian_p = 1;
- goto unpack_integer;
-
- case 'v':
- signed_p = 0;
- integer_size = 2;
- bigendian_p = 0;
- goto unpack_integer;
-
- case 'V':
- signed_p = 0;
- integer_size = 4;
- bigendian_p = 0;
- goto unpack_integer;
-
- unpack_integer:
- if (explicit_endian) {
- bigendian_p = explicit_endian == '>';
- }
+ if (skip_blank(p, type)) continue;
+ p = pack_modifiers(p, type, &natint, &explicit_endian);
+
+ if (p >= pend)
+ len = 1;
+ else if (*p == '*') {
+ star = 1;
+ len = send - s;
+ p++;
+ }
+ else if (ISDIGIT(*p)) {
+ errno = 0;
+ len = STRTOUL(p, (char**)&p, 10);
+ if (len < 0 || errno) {
+ rb_raise(rb_eRangeError, "pack length too big");
+ }
+ }
+ else {
+ len = (type != '@');
+ }
+
+ switch (type) {
+ case '%':
+ rb_raise(rb_eArgError, "%% is not supported");
+ break;
+
+ case 'A':
+ if (len > send - s) len = send - s;
+ {
+ long end = len;
+ char *t = s + len - 1;
+
+ while (t >= s) {
+ if (*t != ' ' && *t != '\0') break;
+ t--; len--;
+ }
+ UNPACK_PUSH(rb_str_new(s, len));
+ s += end;
+ }
+ break;
+
+ case 'Z':
+ {
+ char *t = s;
+
+ if (len > send-s) len = send-s;
+ while (t < s+len && *t) t++;
+ UNPACK_PUSH(rb_str_new(s, t-s));
+ if (t < send) t++;
+ s = star ? t : s+len;
+ }
+ break;
+
+ case 'a':
+ if (len > send - s) len = send - s;
+ UNPACK_PUSH(rb_str_new(s, len));
+ s += len;
+ break;
+
+ case 'b':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 8)
+ len = (send - s) * 8;
+ bits = 0;
+ bitstr = rb_usascii_str_new(0, len);
+ t = RSTRING_PTR(bitstr);
+ for (i=0; i<len; i++) {
+ if (i & 7) bits >>= 1;
+ else bits = (unsigned char)*s++;
+ *t++ = (bits & 1) ? '1' : '0';
+ }
+ UNPACK_PUSH(bitstr);
+ }
+ break;
+
+ case 'B':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 8)
+ len = (send - s) * 8;
+ bits = 0;
+ bitstr = rb_usascii_str_new(0, len);
+ t = RSTRING_PTR(bitstr);
+ for (i=0; i<len; i++) {
+ if (i & 7) bits <<= 1;
+ else bits = (unsigned char)*s++;
+ *t++ = (bits & 128) ? '1' : '0';
+ }
+ UNPACK_PUSH(bitstr);
+ }
+ break;
+
+ case 'h':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 2)
+ len = (send - s) * 2;
+ bits = 0;
+ bitstr = rb_usascii_str_new(0, len);
+ t = RSTRING_PTR(bitstr);
+ for (i=0; i<len; i++) {
+ if (i & 1)
+ bits >>= 4;
+ else
+ bits = (unsigned char)*s++;
+ *t++ = hexdigits[bits & 15];
+ }
+ UNPACK_PUSH(bitstr);
+ }
+ break;
+
+ case 'H':
+ {
+ VALUE bitstr;
+ char *t;
+ int bits;
+ long i;
+
+ if (p[-1] == '*' || len > (send - s) * 2)
+ len = (send - s) * 2;
+ bits = 0;
+ bitstr = rb_usascii_str_new(0, len);
+ t = RSTRING_PTR(bitstr);
+ for (i=0; i<len; i++) {
+ if (i & 1)
+ bits <<= 4;
+ else
+ bits = (unsigned char)*s++;
+ *t++ = hexdigits[(bits >> 4) & 15];
+ }
+ UNPACK_PUSH(bitstr);
+ }
+ break;
+
+ case 'c':
+ signed_p = 1;
+ integer_size = 1;
+ bigendian_p = BIGENDIAN_P(); /* not effective */
+ goto unpack_integer;
+
+ case 'C':
+ signed_p = 0;
+ integer_size = 1;
+ bigendian_p = BIGENDIAN_P(); /* not effective */
+ goto unpack_integer;
+
+ case 's':
+ signed_p = 1;
+ integer_size = NATINT_LEN(short, 2);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'S':
+ signed_p = 0;
+ integer_size = NATINT_LEN(short, 2);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'i':
+ signed_p = 1;
+ integer_size = (int)sizeof(int);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'I':
+ signed_p = 0;
+ integer_size = (int)sizeof(int);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'l':
+ signed_p = 1;
+ integer_size = NATINT_LEN(long, 4);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'L':
+ signed_p = 0;
+ integer_size = NATINT_LEN(long, 4);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'q':
+ signed_p = 1;
+ integer_size = NATINT_LEN_Q;
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'Q':
+ signed_p = 0;
+ integer_size = NATINT_LEN_Q;
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'j':
+ signed_p = 1;
+ integer_size = sizeof(intptr_t);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'J':
+ signed_p = 0;
+ integer_size = sizeof(uintptr_t);
+ bigendian_p = BIGENDIAN_P();
+ goto unpack_integer;
+
+ case 'n':
+ signed_p = 0;
+ integer_size = 2;
+ bigendian_p = 1;
+ goto unpack_integer;
+
+ case 'N':
+ signed_p = 0;
+ integer_size = 4;
+ bigendian_p = 1;
+ goto unpack_integer;
+
+ case 'v':
+ signed_p = 0;
+ integer_size = 2;
+ bigendian_p = 0;
+ goto unpack_integer;
+
+ case 'V':
+ signed_p = 0;
+ integer_size = 4;
+ bigendian_p = 0;
+ goto unpack_integer;
+
+ unpack_integer:
+ if (explicit_endian) {
+ bigendian_p = explicit_endian == '>';
+ }
PACK_LENGTH_ADJUST_SIZE(integer_size);
while (len-- > 0) {
int flags = bigendian_p ? INTEGER_PACK_BIG_ENDIAN : INTEGER_PACK_LITTLE_ENDIAN;
@@ -1416,355 +1255,311 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode)
PACK_ITEM_ADJUST();
break;
- case 'f':
- case 'F':
- PACK_LENGTH_ADJUST_SIZE(sizeof(float));
- while (len-- > 0) {
- float tmp;
- memcpy(&tmp, s, sizeof(float));
- s += sizeof(float);
- UNPACK_PUSH(DBL2NUM((double)tmp));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'e':
- PACK_LENGTH_ADJUST_SIZE(sizeof(float));
- while (len-- > 0) {
- FLOAT_CONVWITH(tmp);
- memcpy(tmp.buf, s, sizeof(float));
- s += sizeof(float);
- VTOHF(tmp);
- UNPACK_PUSH(DBL2NUM(tmp.f));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'E':
- PACK_LENGTH_ADJUST_SIZE(sizeof(double));
- while (len-- > 0) {
- DOUBLE_CONVWITH(tmp);
- memcpy(tmp.buf, s, sizeof(double));
- s += sizeof(double);
- VTOHD(tmp);
- UNPACK_PUSH(DBL2NUM(tmp.d));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'D':
- case 'd':
- PACK_LENGTH_ADJUST_SIZE(sizeof(double));
- while (len-- > 0) {
- double tmp;
- memcpy(&tmp, s, sizeof(double));
- s += sizeof(double);
- UNPACK_PUSH(DBL2NUM(tmp));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'g':
- PACK_LENGTH_ADJUST_SIZE(sizeof(float));
- while (len-- > 0) {
- FLOAT_CONVWITH(tmp);
- memcpy(tmp.buf, s, sizeof(float));
- s += sizeof(float);
- NTOHF(tmp);
- UNPACK_PUSH(DBL2NUM(tmp.f));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'G':
- PACK_LENGTH_ADJUST_SIZE(sizeof(double));
- while (len-- > 0) {
- DOUBLE_CONVWITH(tmp);
- memcpy(tmp.buf, s, sizeof(double));
- s += sizeof(double);
- NTOHD(tmp);
- UNPACK_PUSH(DBL2NUM(tmp.d));
- }
- PACK_ITEM_ADJUST();
- break;
-
- case 'U':
- if (len > send - s) len = send - s;
- while (len > 0 && s < send) {
- long alen = send - s;
- unsigned long l;
-
- l = utf8_to_uv(s, &alen);
- s += alen; len--;
- UNPACK_PUSH(ULONG2NUM(l));
- }
- break;
-
- case 'u':
- {
- VALUE buf = infected_str_new(0, (send - s)*3/4, str);
- char *ptr = RSTRING_PTR(buf);
- long total = 0;
-
- while (s < send && (unsigned char)*s > ' ' && (unsigned char)*s < 'a') {
- long a,b,c,d;
- char hunk[3];
-
- len = ((unsigned char)*s++ - ' ') & 077;
-
- total += len;
- if (total > RSTRING_LEN(buf)) {
- len -= total - RSTRING_LEN(buf);
- total = RSTRING_LEN(buf);
- }
-
- while (len > 0) {
- long mlen = len > 3 ? 3 : len;
-
- if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
- a = ((unsigned char)*s++ - ' ') & 077;
- else
- a = 0;
- if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
- b = ((unsigned char)*s++ - ' ') & 077;
- else
- b = 0;
- if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
- c = ((unsigned char)*s++ - ' ') & 077;
- else
- c = 0;
- if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
- d = ((unsigned char)*s++ - ' ') & 077;
- else
- d = 0;
- hunk[0] = (char)(a << 2 | b >> 4);
- hunk[1] = (char)(b << 4 | c >> 2);
- hunk[2] = (char)(c << 6 | d);
- memcpy(ptr, hunk, mlen);
- ptr += mlen;
- len -= mlen;
- }
- if (s < send && (unsigned char)*s != '\r' && *s != '\n')
- s++; /* possible checksum byte */
- if (s < send && *s == '\r') s++;
- if (s < send && *s == '\n') s++;
- }
-
- rb_str_set_len(buf, total);
- UNPACK_PUSH(buf);
- }
- break;
-
- case 'm':
- {
- VALUE buf = infected_str_new(0, (send - s + 3)*3/4, str); /* +3 is for skipping paddings */
- char *ptr = RSTRING_PTR(buf);
- int a = -1,b = -1,c = 0,d = 0;
- static signed char b64_xtable[256];
-
- if (b64_xtable['/'] <= 0) {
- int i;
-
- for (i = 0; i < 256; i++) {
- b64_xtable[i] = -1;
- }
- for (i = 0; i < 64; i++) {
- b64_xtable[(unsigned char)b64_table[i]] = (char)i;
- }
- }
- if (len == 0) {
- while (s < send) {
- a = b = c = d = -1;
- a = b64_xtable[(unsigned char)*s++];
- if (s >= send || a == -1) rb_raise(rb_eArgError, "invalid base64");
- b = b64_xtable[(unsigned char)*s++];
- if (s >= send || b == -1) rb_raise(rb_eArgError, "invalid base64");
- if (*s == '=') {
- if (s + 2 == send && *(s + 1) == '=') break;
- rb_raise(rb_eArgError, "invalid base64");
- }
- c = b64_xtable[(unsigned char)*s++];
- if (s >= send || c == -1) rb_raise(rb_eArgError, "invalid base64");
- if (s + 1 == send && *s == '=') break;
- d = b64_xtable[(unsigned char)*s++];
- if (d == -1) rb_raise(rb_eArgError, "invalid base64");
- *ptr++ = castchar(a << 2 | b >> 4);
- *ptr++ = castchar(b << 4 | c >> 2);
- *ptr++ = castchar(c << 6 | d);
- }
- if (c == -1) {
- *ptr++ = castchar(a << 2 | b >> 4);
- if (b & 0xf) rb_raise(rb_eArgError, "invalid base64");
- }
- else if (d == -1) {
- *ptr++ = castchar(a << 2 | b >> 4);
- *ptr++ = castchar(b << 4 | c >> 2);
- if (c & 0x3) rb_raise(rb_eArgError, "invalid base64");
- }
- }
- else {
- while (s < send) {
- a = b = c = d = -1;
- while ((a = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
- if (s >= send) break;
- s++;
- while ((b = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
- if (s >= send) break;
- s++;
- while ((c = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
- if (*s == '=' || s >= send) break;
- s++;
- while ((d = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
- if (*s == '=' || s >= send) break;
- s++;
- *ptr++ = castchar(a << 2 | b >> 4);
- *ptr++ = castchar(b << 4 | c >> 2);
- *ptr++ = castchar(c << 6 | d);
- a = -1;
- }
- if (a != -1 && b != -1) {
- if (c == -1)
- *ptr++ = castchar(a << 2 | b >> 4);
- else {
- *ptr++ = castchar(a << 2 | b >> 4);
- *ptr++ = castchar(b << 4 | c >> 2);
- }
- }
- }
- rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
- UNPACK_PUSH(buf);
- }
- break;
-
- case 'M':
- {
- VALUE buf = infected_str_new(0, send - s, str);
- char *ptr = RSTRING_PTR(buf), *ss = s;
- int csum = 0;
- int c1, c2;
-
- while (s < send) {
- if (*s == '=') {
- if (++s == send) break;
- if (s+1 < send && *s == '\r' && *(s+1) == '\n')
- s++;
- if (*s != '\n') {
- if ((c1 = hex2num(*s)) == -1) break;
- if (++s == send) break;
- if ((c2 = hex2num(*s)) == -1) break;
- csum |= *ptr++ = castchar(c1 << 4 | c2);
- }
- }
- else {
- csum |= *ptr++ = *s;
- }
- s++;
- ss = s;
- }
- rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
- rb_str_buf_cat(buf, ss, send-ss);
- csum = ISASCII(csum) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID;
- ENCODING_CODERANGE_SET(buf, rb_ascii8bit_encindex(), csum);
- UNPACK_PUSH(buf);
- }
- break;
-
- case '@':
- if (len > RSTRING_LEN(str))
- rb_raise(rb_eArgError, "@ outside of string");
- s = RSTRING_PTR(str) + len;
- break;
-
- case 'X':
- if (len > s - RSTRING_PTR(str))
- rb_raise(rb_eArgError, "X outside of string");
- s -= len;
- break;
-
- case 'x':
- if (len > send - s)
- rb_raise(rb_eArgError, "x outside of string");
- s += len;
- break;
-
- case 'P':
- if (sizeof(char *) <= (size_t)(send - s)) {
- VALUE tmp = Qnil;
- char *t;
-
- memcpy(&t, s, sizeof(char *));
- s += sizeof(char *);
-
- if (t) {
- VALUE a;
- const VALUE *p, *pend;
-
- if (!(a = str_associated(str))) {
- rb_raise(rb_eArgError, "no associated pointer");
- }
- p = RARRAY_CONST_PTR(a);
- pend = p + RARRAY_LEN(a);
- while (p < pend) {
- if (RB_TYPE_P(*p, T_STRING) && RSTRING_PTR(*p) == t) {
- if (len < RSTRING_LEN(*p)) {
- tmp = rb_tainted_str_new(t, len);
- str_associate(tmp, a);
- }
- else {
- tmp = *p;
- }
- break;
- }
- p++;
- }
- if (p == pend) {
- rb_raise(rb_eArgError, "non associated pointer");
- }
- }
- UNPACK_PUSH(tmp);
- }
- break;
-
- case 'p':
- if (len > (long)((send - s) / sizeof(char *)))
- len = (send - s) / sizeof(char *);
- while (len-- > 0) {
- if ((size_t)(send - s) < sizeof(char *))
- break;
- else {
- VALUE tmp = Qnil;
- char *t;
-
- memcpy(&t, s, sizeof(char *));
- s += sizeof(char *);
-
- if (t) {
- VALUE a;
- const VALUE *p, *pend;
-
- if (!(a = str_associated(str))) {
- rb_raise(rb_eArgError, "no associated pointer");
- }
- p = RARRAY_CONST_PTR(a);
- pend = p + RARRAY_LEN(a);
- while (p < pend) {
- if (RB_TYPE_P(*p, T_STRING) && RSTRING_PTR(*p) == t) {
- tmp = *p;
- break;
- }
- p++;
- }
- if (p == pend) {
- rb_raise(rb_eArgError, "non associated pointer");
- }
- }
- UNPACK_PUSH(tmp);
- }
- }
- break;
-
- case 'w':
- {
+ case 'f':
+ case 'F':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(float));
+ while (len-- > 0) {
+ float tmp;
+ UNPACK_FETCH(&tmp, float);
+ UNPACK_PUSH(DBL2NUM((double)tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'e':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(float));
+ while (len-- > 0) {
+ FLOAT_CONVWITH(tmp);
+ UNPACK_FETCH(tmp.buf, float);
+ VTOHF(tmp);
+ UNPACK_PUSH(DBL2NUM(tmp.f));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'E':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(double));
+ while (len-- > 0) {
+ DOUBLE_CONVWITH(tmp);
+ UNPACK_FETCH(tmp.buf, double);
+ VTOHD(tmp);
+ UNPACK_PUSH(DBL2NUM(tmp.d));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'D':
+ case 'd':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(double));
+ while (len-- > 0) {
+ double tmp;
+ UNPACK_FETCH(&tmp, double);
+ UNPACK_PUSH(DBL2NUM(tmp));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'g':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(float));
+ while (len-- > 0) {
+ FLOAT_CONVWITH(tmp);
+ UNPACK_FETCH(tmp.buf, float);
+ NTOHF(tmp);
+ UNPACK_PUSH(DBL2NUM(tmp.f));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'G':
+ PACK_LENGTH_ADJUST_SIZE(sizeof(double));
+ while (len-- > 0) {
+ DOUBLE_CONVWITH(tmp);
+ UNPACK_FETCH(tmp.buf, double);
+ NTOHD(tmp);
+ UNPACK_PUSH(DBL2NUM(tmp.d));
+ }
+ PACK_ITEM_ADJUST();
+ break;
+
+ case 'U':
+ if (len > send - s) len = send - s;
+ while (len > 0 && s < send) {
+ long alen = send - s;
+ unsigned long l;
+
+ l = utf8_to_uv(s, &alen);
+ s += alen; len--;
+ UNPACK_PUSH(ULONG2NUM(l));
+ }
+ break;
+
+ case 'u':
+ {
+ VALUE buf = rb_str_new(0, (send - s)*3/4);
+ char *ptr = RSTRING_PTR(buf);
+ long total = 0;
+
+ while (s < send && (unsigned char)*s > ' ' && (unsigned char)*s < 'a') {
+ long a,b,c,d;
+ char hunk[3];
+
+ len = ((unsigned char)*s++ - ' ') & 077;
+
+ total += len;
+ if (total > RSTRING_LEN(buf)) {
+ len -= total - RSTRING_LEN(buf);
+ total = RSTRING_LEN(buf);
+ }
+
+ while (len > 0) {
+ long mlen = len > 3 ? 3 : len;
+
+ if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
+ a = ((unsigned char)*s++ - ' ') & 077;
+ else
+ a = 0;
+ if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
+ b = ((unsigned char)*s++ - ' ') & 077;
+ else
+ b = 0;
+ if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
+ c = ((unsigned char)*s++ - ' ') & 077;
+ else
+ c = 0;
+ if (s < send && (unsigned char)*s >= ' ' && (unsigned char)*s < 'a')
+ d = ((unsigned char)*s++ - ' ') & 077;
+ else
+ d = 0;
+ hunk[0] = (char)(a << 2 | b >> 4);
+ hunk[1] = (char)(b << 4 | c >> 2);
+ hunk[2] = (char)(c << 6 | d);
+ memcpy(ptr, hunk, mlen);
+ ptr += mlen;
+ len -= mlen;
+ }
+ if (s < send && (unsigned char)*s != '\r' && *s != '\n')
+ s++; /* possible checksum byte */
+ if (s < send && *s == '\r') s++;
+ if (s < send && *s == '\n') s++;
+ }
+
+ rb_str_set_len(buf, total);
+ UNPACK_PUSH(buf);
+ }
+ break;
+
+ case 'm':
+ {
+ VALUE buf = rb_str_new(0, (send - s + 3)*3/4); /* +3 is for skipping paddings */
+ char *ptr = RSTRING_PTR(buf);
+ int a = -1,b = -1,c = 0,d = 0;
+ static signed char b64_xtable[256];
+
+ if (b64_xtable['/'] <= 0) {
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ b64_xtable[i] = -1;
+ }
+ for (i = 0; i < 64; i++) {
+ b64_xtable[(unsigned char)b64_table[i]] = (char)i;
+ }
+ }
+ if (len == 0) {
+ while (s < send) {
+ a = b = c = d = -1;
+ a = b64_xtable[(unsigned char)*s++];
+ if (s >= send || a == -1) rb_raise(rb_eArgError, "invalid base64");
+ b = b64_xtable[(unsigned char)*s++];
+ if (s >= send || b == -1) rb_raise(rb_eArgError, "invalid base64");
+ if (*s == '=') {
+ if (s + 2 == send && *(s + 1) == '=') break;
+ rb_raise(rb_eArgError, "invalid base64");
+ }
+ c = b64_xtable[(unsigned char)*s++];
+ if (s >= send || c == -1) rb_raise(rb_eArgError, "invalid base64");
+ if (s + 1 == send && *s == '=') break;
+ d = b64_xtable[(unsigned char)*s++];
+ if (d == -1) rb_raise(rb_eArgError, "invalid base64");
+ *ptr++ = castchar(a << 2 | b >> 4);
+ *ptr++ = castchar(b << 4 | c >> 2);
+ *ptr++ = castchar(c << 6 | d);
+ }
+ if (c == -1) {
+ *ptr++ = castchar(a << 2 | b >> 4);
+ if (b & 0xf) rb_raise(rb_eArgError, "invalid base64");
+ }
+ else if (d == -1) {
+ *ptr++ = castchar(a << 2 | b >> 4);
+ *ptr++ = castchar(b << 4 | c >> 2);
+ if (c & 0x3) rb_raise(rb_eArgError, "invalid base64");
+ }
+ }
+ else {
+ while (s < send) {
+ a = b = c = d = -1;
+ while ((a = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
+ if (s >= send) break;
+ s++;
+ while ((b = b64_xtable[(unsigned char)*s]) == -1 && s < send) {s++;}
+ if (s >= send) break;
+ s++;
+ while ((c = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
+ if (*s == '=' || s >= send) break;
+ s++;
+ while ((d = b64_xtable[(unsigned char)*s]) == -1 && s < send) {if (*s == '=') break; s++;}
+ if (*s == '=' || s >= send) break;
+ s++;
+ *ptr++ = castchar(a << 2 | b >> 4);
+ *ptr++ = castchar(b << 4 | c >> 2);
+ *ptr++ = castchar(c << 6 | d);
+ a = -1;
+ }
+ if (a != -1 && b != -1) {
+ if (c == -1)
+ *ptr++ = castchar(a << 2 | b >> 4);
+ else {
+ *ptr++ = castchar(a << 2 | b >> 4);
+ *ptr++ = castchar(b << 4 | c >> 2);
+ }
+ }
+ }
+ rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
+ UNPACK_PUSH(buf);
+ }
+ break;
+
+ case 'M':
+ {
+ VALUE buf = rb_str_new(0, send - s);
+ char *ptr = RSTRING_PTR(buf), *ss = s;
+ int csum = 0;
+ int c1, c2;
+
+ while (s < send) {
+ if (*s == '=') {
+ if (++s == send) break;
+ if (s+1 < send && *s == '\r' && *(s+1) == '\n')
+ s++;
+ if (*s != '\n') {
+ if ((c1 = hex2num(*s)) == -1) break;
+ if (++s == send) break;
+ if ((c2 = hex2num(*s)) == -1) break;
+ csum |= *ptr++ = castchar(c1 << 4 | c2);
+ }
+ }
+ else {
+ csum |= *ptr++ = *s;
+ }
+ s++;
+ ss = s;
+ }
+ rb_str_set_len(buf, ptr - RSTRING_PTR(buf));
+ rb_str_buf_cat(buf, ss, send-ss);
+ csum = ISASCII(csum) ? ENC_CODERANGE_7BIT : ENC_CODERANGE_VALID;
+ ENCODING_CODERANGE_SET(buf, rb_ascii8bit_encindex(), csum);
+ UNPACK_PUSH(buf);
+ }
+ break;
+
+ case '@':
+ if (len > RSTRING_LEN(str))
+ rb_raise(rb_eArgError, "@ outside of string");
+ s = RSTRING_PTR(str) + len;
+ break;
+
+ case 'X':
+ if (len > s - RSTRING_PTR(str))
+ rb_raise(rb_eArgError, "X outside of string");
+ s -= len;
+ break;
+
+ case 'x':
+ if (len > send - s)
+ rb_raise(rb_eArgError, "x outside of string");
+ s += len;
+ break;
+
+ case 'P':
+ if (sizeof(char *) <= (size_t)(send - s)) {
+ VALUE tmp = Qnil;
+ char *t;
+
+ UNPACK_FETCH(&t, char *);
+ if (t) {
+ if (!associates) associates = str_associated(str);
+ tmp = associated_pointer(associates, t);
+ if (len < RSTRING_LEN(tmp)) {
+ tmp = rb_str_new(t, len);
+ str_associate(tmp, associates);
+ }
+ }
+ UNPACK_PUSH(tmp);
+ }
+ break;
+
+ case 'p':
+ if (len > (long)((send - s) / sizeof(char *)))
+ len = (send - s) / sizeof(char *);
+ while (len-- > 0) {
+ if ((size_t)(send - s) < sizeof(char *))
+ break;
+ else {
+ VALUE tmp = Qnil;
+ char *t;
+
+ UNPACK_FETCH(&t, char *);
+ if (t) {
+ if (!associates) associates = str_associated(str);
+ tmp = associated_pointer(associates, t);
+ }
+ UNPACK_PUSH(tmp);
+ }
+ }
+ break;
+
+ case 'w':
+ {
char *s0 = s;
while (len > 0 && s < send) {
if (*s & 0x80) {
@@ -1777,201 +1572,72 @@ pack_unpack_internal(VALUE str, VALUE fmt, int mode)
s0 = s;
}
}
- }
- break;
+ }
+ break;
- default:
+ default:
unknown_directive("unpack", type, fmt);
- break;
- }
+ break;
+ }
}
return ary;
}
-/*
- * call-seq:
- * str.unpack(format) -> anArray
- *
- * Decodes <i>str</i> (which may contain binary data) according to the
- * format string, returning an array of each value extracted. The
- * format string consists of a sequence of single-character directives,
- * summarized in the table at the end of this entry.
- * Each directive may be followed
- * by a number, indicating the number of times to repeat with this
- * directive. An asterisk (``<code>*</code>'') will use up all
- * remaining elements. The directives <code>sSiIlL</code> may each be
- * followed by an underscore (``<code>_</code>'') or
- * exclamation mark (``<code>!</code>'') to use the underlying
- * platform's native size for the specified type; otherwise, it uses a
- * platform-independent consistent size. Spaces are ignored in the
- * format string. See also String#unpack1, Array#pack.
- *
- * "abc \0\0abc \0\0".unpack('A6Z6') #=> ["abc", "abc "]
- * "abc \0\0".unpack('a3a3') #=> ["abc", " \000\000"]
- * "abc \0abc \0".unpack('Z*Z*') #=> ["abc ", "abc "]
- * "aa".unpack('b8B8') #=> ["10000110", "01100001"]
- * "aaa".unpack('h2H2c') #=> ["16", "61", 97]
- * "\xfe\xff\xfe\xff".unpack('sS') #=> [-2, 65534]
- * "now=20is".unpack('M*') #=> ["now is"]
- * "whole".unpack('xax2aX2aX1aX2a') #=> ["h", "e", "l", "l", "o"]
- *
- * This table summarizes the various formats and the Ruby classes
- * returned by each.
- *
- * Integer | |
- * Directive | Returns | Meaning
- * ------------------------------------------------------------------
- * C | Integer | 8-bit unsigned (unsigned char)
- * S | Integer | 16-bit unsigned, native endian (uint16_t)
- * L | Integer | 32-bit unsigned, native endian (uint32_t)
- * Q | Integer | 64-bit unsigned, native endian (uint64_t)
- * J | Integer | pointer width unsigned, native endian (uintptr_t)
- * | |
- * c | Integer | 8-bit signed (signed char)
- * s | Integer | 16-bit signed, native endian (int16_t)
- * l | Integer | 32-bit signed, native endian (int32_t)
- * q | Integer | 64-bit signed, native endian (int64_t)
- * j | Integer | pointer width signed, native endian (intptr_t)
- * | |
- * S_ S! | Integer | unsigned short, native endian
- * I I_ I! | Integer | unsigned int, native endian
- * L_ L! | Integer | unsigned long, native endian
- * Q_ Q! | Integer | unsigned long long, native endian (ArgumentError
- * | | if the platform has no long long type.)
- * J! | Integer | uintptr_t, native endian (same with J)
- * | |
- * s_ s! | Integer | signed short, native endian
- * i i_ i! | Integer | signed int, native endian
- * l_ l! | Integer | signed long, native endian
- * q_ q! | Integer | signed long long, native endian (ArgumentError
- * | | if the platform has no long long type.)
- * j! | Integer | intptr_t, native endian (same with j)
- * | |
- * S> s> S!> s!> | Integer | same as the directives without ">" except
- * L> l> L!> l!> | | big endian
- * I!> i!> | |
- * Q> q> Q!> q!> | | "S>" is same as "n"
- * J> j> J!> j!> | | "L>" is same as "N"
- * | |
- * S< s< S!< s!< | Integer | same as the directives without "<" except
- * L< l< L!< l!< | | little endian
- * I!< i!< | |
- * Q< q< Q!< q!< | | "S<" is same as "v"
- * J< j< J!< j!< | | "L<" is same as "V"
- * | |
- * 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
- * V | Integer | 32-bit unsigned, VAX (little-endian) byte order
- * | |
- * U | Integer | UTF-8 character
- * w | Integer | BER-compressed integer (see Array.pack)
- *
- * Float | |
- * Directive | Returns | Meaning
- * -----------------------------------------------------------------
- * D d | Float | double-precision, native format
- * F f | Float | single-precision, native format
- * E | Float | double-precision, little-endian byte order
- * e | Float | single-precision, little-endian byte order
- * G | Float | double-precision, network (big-endian) byte order
- * g | Float | single-precision, network (big-endian) byte order
- *
- * String | |
- * Directive | Returns | Meaning
- * -----------------------------------------------------------------
- * A | String | arbitrary binary string (remove trailing nulls and ASCII spaces)
- * a | String | arbitrary binary string
- * Z | String | null-terminated string
- * B | String | bit string (MSB first)
- * b | String | bit string (LSB first)
- * H | String | hex string (high nibble first)
- * h | String | hex string (low nibble first)
- * u | String | UU-encoded string
- * M | String | quoted-printable, MIME encoding (see RFC2045)
- * m | String | base64 encoded string (RFC 2045) (default)
- * | | base64 encoded string (RFC 4648) if followed by 0
- * P | String | pointer to a structure (fixed-length string)
- * p | String | pointer to a null-terminated string
- *
- * Misc. | |
- * Directive | Returns | Meaning
- * -----------------------------------------------------------------
- * @ | --- | skip to the offset given by the length argument
- * X | --- | skip backward one byte
- * x | --- | skip forward one byte
- *
- * HISTORY
- *
- * * J, J! j, and j! are available since Ruby 2.3.
- * * Q_, Q!, q_, and q! are available since Ruby 2.1.
- * * I!<, i!<, I!>, and i!> are available since Ruby 1.9.3.
- */
-
static VALUE
-pack_unpack(VALUE str, VALUE fmt)
+pack_unpack(rb_execution_context_t *ec, VALUE str, VALUE fmt, VALUE offset)
{
- int mode = rb_block_given_p() ? UNPACK_BLOCK : UNPACK_ARRAY;
- return pack_unpack_internal(str, fmt, mode);
+ enum unpack_mode mode = rb_block_given_p() ? UNPACK_BLOCK : UNPACK_ARRAY;
+ return pack_unpack_internal(str, fmt, mode, RB_NUM2LONG(offset));
}
-/*
- * call-seq:
- * str.unpack1(format) -> obj
- *
- * Decodes <i>str</i> (which may contain binary data) according to the
- * format string, returning the first value extracted.
- * See also String#unpack, Array#pack.
- */
-
static VALUE
-pack_unpack1(VALUE str, VALUE fmt)
+pack_unpack1(rb_execution_context_t *ec, VALUE str, VALUE fmt, VALUE offset)
{
- return pack_unpack_internal(str, fmt, UNPACK_1);
+ return pack_unpack_internal(str, fmt, UNPACK_1, RB_NUM2LONG(offset));
}
int
rb_uv_to_utf8(char buf[6], unsigned long uv)
{
if (uv <= 0x7f) {
- buf[0] = (char)uv;
- return 1;
+ buf[0] = (char)uv;
+ return 1;
}
if (uv <= 0x7ff) {
- buf[0] = castchar(((uv>>6)&0xff)|0xc0);
- buf[1] = castchar((uv&0x3f)|0x80);
- return 2;
+ buf[0] = castchar(((uv>>6)&0xff)|0xc0);
+ buf[1] = castchar((uv&0x3f)|0x80);
+ return 2;
}
if (uv <= 0xffff) {
- buf[0] = castchar(((uv>>12)&0xff)|0xe0);
- buf[1] = castchar(((uv>>6)&0x3f)|0x80);
- buf[2] = castchar((uv&0x3f)|0x80);
- return 3;
+ buf[0] = castchar(((uv>>12)&0xff)|0xe0);
+ buf[1] = castchar(((uv>>6)&0x3f)|0x80);
+ buf[2] = castchar((uv&0x3f)|0x80);
+ return 3;
}
if (uv <= 0x1fffff) {
- buf[0] = castchar(((uv>>18)&0xff)|0xf0);
- buf[1] = castchar(((uv>>12)&0x3f)|0x80);
- buf[2] = castchar(((uv>>6)&0x3f)|0x80);
- buf[3] = castchar((uv&0x3f)|0x80);
- return 4;
+ buf[0] = castchar(((uv>>18)&0xff)|0xf0);
+ buf[1] = castchar(((uv>>12)&0x3f)|0x80);
+ buf[2] = castchar(((uv>>6)&0x3f)|0x80);
+ buf[3] = castchar((uv&0x3f)|0x80);
+ return 4;
}
if (uv <= 0x3ffffff) {
- buf[0] = castchar(((uv>>24)&0xff)|0xf8);
- buf[1] = castchar(((uv>>18)&0x3f)|0x80);
- buf[2] = castchar(((uv>>12)&0x3f)|0x80);
- buf[3] = castchar(((uv>>6)&0x3f)|0x80);
- buf[4] = castchar((uv&0x3f)|0x80);
- return 5;
+ buf[0] = castchar(((uv>>24)&0xff)|0xf8);
+ buf[1] = castchar(((uv>>18)&0x3f)|0x80);
+ buf[2] = castchar(((uv>>12)&0x3f)|0x80);
+ buf[3] = castchar(((uv>>6)&0x3f)|0x80);
+ buf[4] = castchar((uv&0x3f)|0x80);
+ return 5;
}
if (uv <= 0x7fffffff) {
- buf[0] = castchar(((uv>>30)&0xff)|0xfc);
- buf[1] = castchar(((uv>>24)&0x3f)|0x80);
- buf[2] = castchar(((uv>>18)&0x3f)|0x80);
- buf[3] = castchar(((uv>>12)&0x3f)|0x80);
- buf[4] = castchar(((uv>>6)&0x3f)|0x80);
- buf[5] = castchar((uv&0x3f)|0x80);
- return 6;
+ buf[0] = castchar(((uv>>30)&0xff)|0xfc);
+ buf[1] = castchar(((uv>>24)&0x3f)|0x80);
+ buf[2] = castchar(((uv>>18)&0x3f)|0x80);
+ buf[3] = castchar(((uv>>12)&0x3f)|0x80);
+ buf[4] = castchar(((uv>>6)&0x3f)|0x80);
+ buf[5] = castchar((uv&0x3f)|0x80);
+ return 6;
}
rb_raise(rb_eRangeError, "pack(U): value out of range");
@@ -1996,12 +1662,12 @@ utf8_to_uv(const char *p, long *lenp)
long n;
if (!(uv & 0x80)) {
- *lenp = 1;
+ *lenp = 1;
return uv;
}
if (!(uv & 0x40)) {
- *lenp = 1;
- rb_raise(rb_eArgError, "malformed UTF-8 character");
+ *lenp = 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
}
if (!(uv & 0x20)) { n = 2; uv &= 0x1f; }
@@ -2010,40 +1676,38 @@ utf8_to_uv(const char *p, long *lenp)
else if (!(uv & 0x04)) { n = 5; uv &= 0x03; }
else if (!(uv & 0x02)) { n = 6; uv &= 0x01; }
else {
- *lenp = 1;
- rb_raise(rb_eArgError, "malformed UTF-8 character");
+ *lenp = 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
}
if (n > *lenp) {
- rb_raise(rb_eArgError, "malformed UTF-8 character (expected %ld bytes, given %ld bytes)",
- n, *lenp);
+ rb_raise(rb_eArgError, "malformed UTF-8 character (expected %ld bytes, given %ld bytes)",
+ n, *lenp);
}
*lenp = n--;
if (n != 0) {
- while (n--) {
- c = *p++ & 0xff;
- if ((c & 0xc0) != 0x80) {
- *lenp -= n + 1;
- rb_raise(rb_eArgError, "malformed UTF-8 character");
- }
- else {
- c &= 0x3f;
- uv = uv << 6 | c;
- }
- }
+ while (n--) {
+ c = *p++ & 0xff;
+ if ((c & 0xc0) != 0x80) {
+ *lenp -= n + 1;
+ rb_raise(rb_eArgError, "malformed UTF-8 character");
+ }
+ else {
+ c &= 0x3f;
+ uv = uv << 6 | c;
+ }
+ }
}
n = *lenp - 1;
if (uv < utf8_limits[n]) {
- rb_raise(rb_eArgError, "redundant UTF-8 sequence");
+ rb_raise(rb_eArgError, "redundant UTF-8 sequence");
}
return uv;
}
+#include "pack.rbinc"
+
void
Init_pack(void)
{
- rb_define_method(rb_cArray, "pack", pack_pack, -1);
- rb_define_method(rb_cString, "unpack", pack_unpack, 1);
- rb_define_method(rb_cString, "unpack1", pack_unpack1, 1);
-
id_associated = rb_make_internal_id();
}