diff options
Diffstat (limited to 'pack.c')
| -rw-r--r-- | pack.c | 134 |
1 files changed, 112 insertions, 22 deletions
@@ -19,6 +19,7 @@ #include "internal.h" #include "internal/array.h" #include "internal/bits.h" +#include "internal/numeric.h" #include "internal/string.h" #include "internal/symbol.h" #include "internal/variable.h" @@ -61,7 +62,7 @@ is_bigendian(void) { static int init = 0; static int endian_value; - char *p; + const char *p; if (init) return endian_value; init = 1; @@ -118,6 +119,7 @@ typedef union { #define MAX_INTEGER_PACK_SIZE 8 static const char toofew[] = "too few arguments"; +static const char intoitself[] = "cannot pack buffer object into itself"; static void encodes(VALUE,const char*,long,int,int); static void qpencode(VALUE,VALUE,long); @@ -279,6 +281,8 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) #define MORE_ITEM (idx < RARRAY_LEN(ary)) #define THISFROM (MORE_ITEM ? RARRAY_AREF(ary, idx) : TOO_FEW) #define NEXTFROM (MORE_ITEM ? RARRAY_AREF(ary, idx++) : TOO_FEW) +#define NOT_BUFFER(val) (((val) == res) ? rb_raise(rb_eArgError, intoitself) : (void)0) +#define STR_FROM(val) NOT_BUFFER(StringValue(val)) while (p < pend) { int explicit_endian = 0; @@ -302,7 +306,7 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) else if (ISDIGIT(*p)) { errno = 0; len = STRTOUL(p, (char**)&p, 10); - if (errno) { + if (len < 0 || errno) { rb_raise(rb_eRangeError, "pack length too big"); } } @@ -333,7 +337,7 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) plen = 0; } else { - StringValue(from); + STR_FROM(from); ptr = RSTRING_PTR(from); plen = RSTRING_LEN(from); } @@ -667,10 +671,58 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) } break; + case 'r': /* r for SLEB128 encoding (signed) */ + case 'R': /* R for ULEB128 encoding (unsigned) */ + { + int pack_flags = INTEGER_PACK_LITTLE_ENDIAN; + + if (type == 'r') { + pack_flags |= INTEGER_PACK_2COMP; + } + + while (len-- > 0) { + size_t numbytes, nlz_bits; + int sign, extra = 0; + char *cp; + const long start = RSTRING_LEN(res); + + from = NEXTFROM; + from = rb_to_int(from); + if (type == 'R' && rb_int_negative_p(from)) { + rb_raise(rb_eArgError, "can't encode negative numbers in ULEB128"); + } + + numbytes = rb_absint_numwords(from, 7, &nlz_bits); + if (numbytes == 0) { + numbytes = 1; + } + else if (nlz_bits == 0 && type == 'r') { + /* No leading zero bits, we need an extra byte for sign extension */ + extra = 1; + } + rb_str_modify_expand(res, numbytes + extra); + + cp = RSTRING_PTR(res) + start; + sign = rb_integer_pack(from, cp, numbytes, 1, 1, pack_flags); + + if (extra) { + /* Need an extra byte */ + cp[numbytes++] = sign < 0 ? 0x7f : 0x00; + } + rb_str_set_len(res, start + numbytes); + + while (1 < numbytes) { + *cp |= 0x80; + cp++; + numbytes--; + } + } + } + break; case 'u': /* uuencoded string */ case 'm': /* base64 encoded string */ from = NEXTFROM; - StringValue(from); + STR_FROM(from); ptr = RSTRING_PTR(from); plen = RSTRING_LEN(from); @@ -700,6 +752,7 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) case 'M': /* quoted-printable encoded string */ from = rb_obj_as_string(NEXTFROM); + NOT_BUFFER(from); if (len <= 1) len = 72; qpencode(res, from, len); @@ -708,7 +761,7 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) case 'P': /* pointer to packed byte string */ from = THISFROM; if (!NIL_P(from)) { - StringValue(from); + STR_FROM(from); if (RSTRING_LEN(from) < len) { rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)", RSTRING_LEN(from), len); @@ -718,13 +771,11 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) /* FALL THROUGH */ case 'p': /* pointer to string */ while (len-- > 0) { - char *t; + const char *t = 0; from = NEXTFROM; - if (NIL_P(from)) { - t = 0; - } - else { - t = StringValuePtr(from); + if (!NIL_P(from)) { + STR_FROM(from); + t = RSTRING_PTR(from); } if (!associates) { associates = rb_ary_new(); @@ -736,7 +787,7 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) case 'w': /* BER compressed integer */ while (len-- > 0) { - VALUE buf = rb_str_new(0, 0); + VALUE buf; size_t numbytes; int sign; char *cp; @@ -950,8 +1001,8 @@ static VALUE pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset) { #define hexdigits ruby_hexdigits - char *s, *send; - char *p, *pend; + const char *s, *send; + const char *p, *pend; VALUE ary, associates = Qfalse; long len; AVOID_CC_BUG long tmp_len; @@ -973,9 +1024,10 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset) 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"); + if (offset < 0 ? (offset += len) < 0 : offset > len) { + rb_raise(rb_eArgError, "offset outside of string"); + } s = RSTRING_PTR(str); send = s + len; @@ -1025,7 +1077,7 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset) if (len > send - s) len = send - s; { long end = len; - char *t = s + len - 1; + const char *t = s + len - 1; while (t >= s) { if (*t != ' ' && *t != '\0') break; @@ -1038,7 +1090,7 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset) case 'Z': { - char *t = s; + const char *t = s; if (len > send-s) len = send-s; while (t < s+len && *t) t++; @@ -1472,7 +1524,8 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset) case 'M': { VALUE buf = rb_str_new(0, send - s); - char *ptr = RSTRING_PTR(buf), *ss = s; + char *ptr = RSTRING_PTR(buf); + const char *ss = s; int csum = 0; int c1, c2; @@ -1520,10 +1573,14 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset) s += len; break; + case '^': + UNPACK_PUSH(SSIZET2NUM(s - RSTRING_PTR(str))); + break; + case 'P': if (sizeof(char *) <= (size_t)(send - s)) { VALUE tmp = Qnil; - char *t; + const char *t; UNPACK_FETCH(&t, char *); if (t) { @@ -1546,7 +1603,7 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset) break; else { VALUE tmp = Qnil; - char *t; + const char *t; UNPACK_FETCH(&t, char *); if (t) { @@ -1558,9 +1615,42 @@ pack_unpack_internal(VALUE str, VALUE fmt, enum unpack_mode mode, long offset) } break; + case 'r': + case 'R': + { + int pack_flags = INTEGER_PACK_LITTLE_ENDIAN; + + if (type == 'r') { + pack_flags |= INTEGER_PACK_2COMP; + } + const char *s0 = s; + while (len > 0 && s < send) { + if (*s & 0x80) { + s++; + } + else { + s++; + UNPACK_PUSH(rb_integer_unpack(s0, s-s0, 1, 1, pack_flags)); + len--; + s0 = s; + } + } + /* Handle incomplete value and remaining expected values with nil (only if not using *) */ + if (!star) { + if (s0 != s && len > 0) { + UNPACK_PUSH(Qnil); + len--; + } + while (len-- > 0) { + UNPACK_PUSH(Qnil); + } + } + } + break; + case 'w': { - char *s0 = s; + const char *s0 = s; while (len > 0 && s < send) { if (*s & 0x80) { s++; |
