diff options
Diffstat (limited to 'pack.c')
-rw-r--r-- | pack.c | 883 |
1 files changed, 883 insertions, 0 deletions
diff --git a/pack.c b/pack.c new file mode 100644 index 0000000000..35052e3406 --- /dev/null +++ b/pack.c @@ -0,0 +1,883 @@ +/************************************************ + + pack.c - + + $Author$ + $Date$ + created at: Thu Feb 10 15:17:05 JST 1994 + + Copyright (C) 1993-1996 Yukihiro Matsumoto + +************************************************/ + +#include "ruby.h" +#include <ctype.h> +#include <sys/types.h> + +#define swaps(x) ((((x)&0xFF)<<8) + (((x)>>8)&0xFF)) +#define swapl(x) ((((x)&0xFF)<<24) \ + +(((x)>>24)&0xFF) \ + +(((x)&0x0000FF00)<<8) \ + +(((x)&0x00FF0000)>>8) ) + +#ifdef DYNAMIC_ENDIAN +#ifdef ntohs +#undef ntohs +#undef ntohl +#undef htons +#undef htonl +#endif +static int +endian() +{ + static int init = 0; + static int endian_value; + char *p; + + if (init) return endian_value; + init = 1; + p = (char*)&init; + return endian_value = p[0]?0:1; +} + +#define ntohs(x) (endian()?(x):swaps(x)) +#define ntohl(x) (endian()?(x):swapl(x)) +#define htons(x) (endian()?(x):swaps(x)) +#define htonl(x) (endian()?(x):swapl(x)) +#define htovs(x) (endian()?swaps(x):(x)) +#define htovl(x) (endian()?swapl(x):(x)) +#define vtohs(x) (endian()?swaps(x):(x)) +#define vtohl(x) (endian()?swapl(x):(x)) +#else +#ifdef WORDS_BIGENDIAN +#ifndef ntohs +#define ntohs(x) (x) +#define ntohl(x) (x) +#define htons(x) (x) +#define htonl(x) (x) +#endif +#define htovs(x) swaps(x) +#define htovl(x) swapl(x) +#define vtohs(x) swaps(x) +#define vtohl(x) swapl(x) +#else /* LITTLE ENDIAN */ +#ifndef ntohs +#define ntohs(x) swaps(x) +#define ntohl(x) swapl(x) +#define htons(x) swaps(x) +#define htonl(x) swapl(x) +#endif +#define htovs(x) (x) +#define htovl(x) (x) +#define vtohs(x) (x) +#define vtohl(x) (x) +#endif +#endif + +extern VALUE cString, cArray; +#ifndef atof +double atof(); +#endif + +static char *toofew = "too few arguments"; + +static void encodes(); + +static VALUE +pack_pack(ary, fmt) + struct RArray *ary; + struct RString *fmt; +{ + static char *nul10 = "\0\0\0\0\0\0\0\0\0\0"; + static char *spc10 = " "; + UCHAR *p, *pend; + VALUE res, from; + char type; + int items, len, idx; + UCHAR *ptr; + int plen; + + Check_Type(fmt, T_STRING); + + p = fmt->ptr; + pend = fmt->ptr + fmt->len; + res = str_new(0, 0); + + items = ary->len; + idx = 0; + +#define NEXTFROM (items-- > 0 ? ary->ptr[idx++] : (ArgError(toofew),0)) + + while (p < pend) { + type = *p++; /* get data type */ + + if (*p == '*') { /* set data length */ + len = strchr("@Xxu", type) ? 0 : items; + p++; + } + else if (isdigit(*p)) { + len = strtoul(p, (char**)&p, 10); + } + else { + len = 1; + } + + switch (type) { + case 'A': case 'a': + case 'B': case 'b': + case 'H': case 'h': + from = NEXTFROM; + if (NIL_P(from)) { + ptr = 0; + plen = 0; + } + else { + from = obj_as_string(from); + ptr = RSTRING(from)->ptr; + plen = RSTRING(from)->len; + } + + if (p[-1] == '*') + len = plen; + + switch (type) { + case 'a': + case 'A': + if (plen >= len) + str_cat(res, ptr, len); + else { + str_cat(res, ptr, plen); + len -= plen; + while (len >= 10) { + str_cat(res, (type == 'A')?spc10:nul10, 10); + len -= 10; + } + str_cat(res, (type == 'A')?spc10:nul10, len); + } + break; + + case 'b': + { + int byte = 0; + int i; + + for (i=0; i++ < len; ptr++) { + if (*ptr & 1) + byte |= 128; + if (i & 7) + byte >>= 1; + else { + char c = byte & 0xff; + str_cat(res, &c, 1); + byte = 0; + } + } + if (len & 7) { + char c; + byte >>= 7 - (len & 7); + c = byte & 0xff; + str_cat(res, &c, 1); + } + } + break; + + case 'B': + { + int byte = 0; + int i; + + for (i=0; i++ < len; ptr++) { + byte |= *ptr & 1; + if (i & 7) + byte <<= 1; + else { + char c = byte & 0xff; + str_cat(res, &c, 1); + byte = 0; + } + } + if (len & 7) { + char c; + byte <<= 7 - (len & 7); + c = byte & 0xff; + str_cat(res, &c, 1); + } + } + break; + + case 'h': + { + int byte = 0; + int i; + + for (i=0; i++ < len; ptr++) { + if (isxdigit(*ptr)) { + if (isalpha(*ptr)) + byte |= (((*ptr & 15) + 9) & 15) << 4; + else + byte |= (*ptr & 15) << 4; + if (i & 1) + byte >>= 4; + else { + char c = byte & 0xff; + str_cat(res, &c, 1); + byte = 0; + } + } + } + if (len & 1) { + char c = byte & 0xff; + str_cat(res, &c, 1); + } + } + break; + + case 'H': + { + int byte = 0; + int i; + + for (i=0; i++ < len; ptr++) { + if (isxdigit(*ptr)) { + if (isalpha(*ptr)) + byte |= ((*ptr & 15) + 9) & 15; + else + byte |= *ptr & 15; + if (i & 1) + byte <<= 4; + else { + char c = byte & 0xff; + str_cat(res, &c, 1); + byte = 0; + } + } + } + if (len & 1) { + char c = byte & 0xff; + str_cat(res, &c, 1); + } + } + break; + } + break; + + case 'c': + case 'C': + while (len-- > 0) { + char c; + + from = NEXTFROM; + if (NIL_P(from)) c = 0; + else { + c = NUM2INT(from); + } + str_cat(res, &c, sizeof(char)); + } + break; + + case 's': + case 'S': + while (len-- > 0) { + short s; + + from = NEXTFROM; + if (NIL_P(from)) s = 0; + else { + s = NUM2INT(from); + } + str_cat(res, &s, sizeof(short)); + } + break; + + case 'i': + case 'I': + while (len-- > 0) { + int i; + + from = NEXTFROM; + if (NIL_P(from)) i = 0; + else { + i = NUM2INT(from); + } + str_cat(res, &i, sizeof(int)); + } + break; + + case 'l': + case 'L': + while (len-- > 0) { + long l; + + from = NEXTFROM; + if (NIL_P(from)) l = 0; + else { + l = NUM2INT(from); + } + str_cat(res, &l, sizeof(long)); + } + break; + + case 'n': + while (len-- > 0) { + unsigned short s; + + from = NEXTFROM; + if (NIL_P(from)) s = 0; + else { + s = NUM2INT(from); + } + s = htons(s); + str_cat(res, &s, sizeof(short)); + } + break; + + case 'N': + while (len-- > 0) { + unsigned long l; + + from = NEXTFROM; + if (NIL_P(from)) l = 0; + else { + l = NUM2INT(from); + } + l = htonl(l); + str_cat(res, &l, sizeof(long)); + } + break; + + case 'v': + while (len-- > 0) { + unsigned short s; + + from = NEXTFROM; + if (NIL_P(from)) s = 0; + else { + s = NUM2INT(from); + } + s = htovs(s); + str_cat(res, &s, sizeof(short)); + } + break; + + case 'V': + while (len-- > 0) { + unsigned long l; + + from = NEXTFROM; + if (NIL_P(from)) l = 0; + else { + l = NUM2INT(from); + } + l = htovl(l); + str_cat(res, &l, sizeof(long)); + } + break; + + case 'f': + case 'F': + while (len-- > 0) { + float f; + + from = NEXTFROM; + switch (TYPE(from)) { + case T_FLOAT: + f = RFLOAT(from)->value; + break; + case T_STRING: + f = atof(RSTRING(from)->ptr); + default: + f = (float)NUM2INT(from); + break; + } + str_cat(res, &f, sizeof(float)); + } + break; + + case 'd': + case 'D': + while (len-- > 0) { + double d; + + from = NEXTFROM; + switch (TYPE(from)) { + case T_FLOAT: + d = RFLOAT(from)->value; + break; + case T_STRING: + d = atof(RSTRING(from)->ptr); + default: + d = (double)NUM2INT(from); + break; + } + str_cat(res, &d, sizeof(double)); + } + break; + + case 'x': + grow: + while (len >= 10) { + str_cat(res, nul10, 10); + len -= 10; + } + str_cat(res, nul10, len); + break; + + case 'X': + shrink: + if (RSTRING(res)->len < len) + ArgError("X outside of string"); + RSTRING(res)->len -= len; + RSTRING(res)->ptr[RSTRING(res)->len] = '\0'; + break; + + case '@': + len -= RSTRING(res)->len; + if (len > 0) goto grow; + len = -len; + if (len > 0) goto shrink; + break; + + case '%': + ArgError("% may only be used in unpack"); + break; + + case 'u': + from = obj_as_string(NEXTFROM); + ptr = RSTRING(from)->ptr; + plen = RSTRING(from)->len; + + if (len <= 1) + len = 45; + else + len = len / 3 * 3; + while (plen > 0) { + int todo; + + if (plen > len) + todo = len; + else + todo = plen; + encodes(res, ptr, todo); + plen -= todo; + ptr += todo; + } + break; + + default: + break; + } + } + + return res; +} + +static void +encodes(str, s, len) + struct RString *str; + UCHAR *s; + int len; +{ + char hunk[4]; + UCHAR *p, *pend; + + *hunk = len + ' '; + str_cat(str, hunk, 1); + while (len > 0) { + hunk[0] = ' ' + (077 & (*s >> 2)); + hunk[1] = ' ' + (077 & (((*s << 4) & 060) | ((s[1] >> 4) & 017))); + hunk[2] = ' ' + (077 & (((s[1] << 2) & 074) | ((s[2] >> 6) & 03))); + hunk[3] = ' ' + (077 & (s[2] & 077)); + str_cat(str, hunk, 4); + s += 3; + len -= 3; + } + p = str->ptr; + pend = str->ptr + str->len; + while (p < pend) { + if (*p == ' ') + *p = '`'; + p++; + } + str_cat(str, "\n", 1); +} + +static VALUE +pack_unpack(str, fmt) + struct RString *str, *fmt; +{ + static char *hexdigits = "0123456789abcdef0123456789ABCDEFx"; + UCHAR *s, *send; + UCHAR *p, *pend; + VALUE ary; + char type; + int len; + + Check_Type(fmt, T_STRING); + + s = str->ptr; + send = s + str->len; + p = fmt->ptr; + pend = p + fmt->len; + + ary = ary_new(); + while (p < pend) { + type = *p++; + if (*p == '*') { + len = send - s; + p++; + } + else if (isdigit(*p)) { + len = strtoul(p, (char**)&p, 10); + } + else { + len = (type != '@'); + } + + switch (type) { + case '%': + ArgError("% is not supported(yet)"); + break; + + case 'A': + if (len > send - s) len = send - s; + { + int end = len; + UCHAR *t = s + len - 1; + + while (t >= s) { + if (*t != ' ' && *t != '\0') break; + t--; + len--; + } + ary_push(ary, str_new(s, len)); + s += end; + } + break; + + case 'a': + if (len > send - s) len = send - s; + ary_push(ary, str_new(s, len)); + s += len; + break; + + case 'b': + { + VALUE bitstr; + UCHAR *t; + int bits, i; + + if (p[-1] == '*' || len > (send - s) * 8) + len = (send - s) * 8; + bits = 0; + ary_push(ary, bitstr = str_new(0, len)); + t = RSTRING(bitstr)->ptr; + for (i=0; i<len; i++) { + if (i & 7) bits >>= 1; + else bits = *s++; + *t++ = (bits & 1) ? '1' : '0'; + } + } + break; + + case 'B': + { + VALUE bitstr; + UCHAR *t; + int bits, i; + + if (p[-1] == '*' || len > (send - s) * 8) + len = (send - s) * 8; + bits = 0; + ary_push(ary, bitstr = str_new(0, len)); + t = RSTRING(bitstr)->ptr; + for (i=0; i<len; i++) { + if (i & 7) bits <<= 1; + else bits = *s++; + *t++ = (bits & 128) ? '1' : '0'; + } + } + break; + + case 'h': + { + VALUE bitstr; + UCHAR *t; + int bits, i; + + if (p[-1] == '*' || len > (send - s) * 2) + len = (send - s) * 2; + bits = 0; + ary_push(ary, bitstr = str_new(0, len)); + t = RSTRING(bitstr)->ptr; + for (i=0; i<len; i++) { + if (i & 1) + bits >>= 4; + else + bits = *s++; + *t++ = hexdigits[bits & 15]; + } + } + break; + + case 'H': + { + VALUE bitstr; + UCHAR *t; + int bits, i; + + if (p[-1] == '*' || len > (send - s) * 2) + len = (send - s) * 2; + bits = 0; + ary_push(ary, bitstr = str_new(0, len)); + t = RSTRING(bitstr)->ptr; + for (i=0; i<len; i++) { + if (i & 1) + bits <<= 4; + else + bits = *s++; + *t++ = hexdigits[(bits >> 4) & 15]; + } + } + break; + + case 'c': + if (len > send - s) + len = send - s; + while (len-- > 0) { + int c = *s++; + if (c > (char)127) c-=256; + ary_push(ary, INT2FIX(c)); + } + break; + + case 'C': + if (len > send - s) + len = send - s; + while (len-- > 0) { + UCHAR c = *s++; + ary_push(ary, INT2FIX(c)); + } + break; + + case 's': + if (len >= (send - s) / sizeof(short)) + len = (send - s) / sizeof(short); + while (len-- > 0) { + short tmp; + memcpy(&tmp, s, sizeof(short)); + s += sizeof(short); + ary_push(ary, INT2FIX(tmp)); + } + break; + + case 'S': + if (len >= (send - s) / sizeof(short)) + len = (send - s) / sizeof(short); + while (len-- > 0) { + unsigned short tmp; + memcpy(&tmp, s, sizeof(short)); + s += sizeof(short); + ary_push(ary, INT2FIX(tmp)); + } + break; + + case 'i': + if (len >= (send - s) / sizeof(int)) + len = (send - s) / sizeof(int); + while (len-- > 0) { + int tmp; + memcpy(&tmp, s, sizeof(int)); + s += sizeof(int); + ary_push(ary, int2inum(tmp)); + } + break; + + case 'I': + if (len >= (send - s) / sizeof(int)) + len = (send - s) / sizeof(int); + while (len-- > 0) { + unsigned int tmp; + memcpy(&tmp, s, sizeof(int)); + s += sizeof(int); + ary_push(ary, int2inum(tmp)); + } + break; + + case 'l': + if (len >= (send - s) / sizeof(long)) + len = (send - s) / sizeof(long); + while (len-- > 0) { + long tmp; + memcpy(&tmp, s, sizeof(long)); + s += sizeof(long); + ary_push(ary, int2inum(tmp)); + } + break; + + case 'L': + if (len >= (send - s) / sizeof(long)) + len = (send - s) / sizeof(long); + while (len-- > 0) { + unsigned long tmp; + memcpy(&tmp, s, sizeof(long)); + s += sizeof(long); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'n': + if (len >= (send - s) / sizeof(short)) + len = (send - s) / sizeof(short); + while (len-- > 0) { + unsigned short tmp; + memcpy(&tmp, s, sizeof(short)); + s += sizeof(short); + tmp = ntohs(tmp); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'N': + if (len >= (send - s) / sizeof(long)) + len = (send - s) / sizeof(long); + while (len-- > 0) { + unsigned long tmp; + memcpy(&tmp, s, sizeof(long)); + s += sizeof(long); + tmp = ntohl(tmp); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'v': + if (len >= (send - s) / sizeof(short)) + len = (send - s) / sizeof(short); + while (len-- > 0) { + unsigned short tmp; + memcpy(&tmp, s, sizeof(short)); + s += sizeof(short); + tmp = vtohs(tmp); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'V': + if (len >= (send - s) / sizeof(long)) + len = (send - s) / sizeof(long); + while (len-- > 0) { + unsigned long tmp; + memcpy(&tmp, s, sizeof(long)); + s += sizeof(long); + tmp = vtohl(tmp); + ary_push(ary, uint2inum(tmp)); + } + break; + + case 'f': + case 'F': + if (len >= (send - s) / sizeof(float)) + len = (send - s) / sizeof(float); + while (len-- > 0) { + float tmp; + memcpy(&tmp, s, sizeof(float)); + s += sizeof(float); + ary_push(ary, float_new((double)tmp)); + } + break; + + case 'D': + case 'd': + if (len >= (send - s) / sizeof(double)) + len = (send - s) / sizeof(double); + while (len-- > 0) { + double tmp; + memcpy(&tmp, s, sizeof(double)); + s += sizeof(double); + ary_push(ary, float_new(tmp)); + } + break; + + case 'u': + { + VALUE str = str_new(0, (send - s)*3/4); + UCHAR *ptr = RSTRING(str)->ptr; + int total = 0; + + while (s < send && *s > ' ' && *s < 'a') { + long a,b,c,d; + char hunk[4]; + + hunk[3] = '\0'; + len = (*s++ - ' ') & 077; + total += len; + if (total > RSTRING(str)->len) { + len -= total - RSTRING(str)->len; + total = RSTRING(str)->len; + } + + while (len > 0) { + int mlen = len > 3 ? 3 : len; + + if (s < send && *s >= ' ') + a = (*s++ - ' ') & 077; + else + a = 0; + if (s < send && *s >= ' ') + b = (*s++ - ' ') & 077; + else + b = 0; + if (s < send && *s >= ' ') + c = (*s++ - ' ') & 077; + else + c = 0; + if (s < send && *s >= ' ') + d = (*s++ - ' ') & 077; + else + d = 0; + hunk[0] = a << 2 | b >> 4; + hunk[1] = b << 4 | c >> 2; + hunk[2] = c << 6 | d; + memcpy(ptr, hunk, mlen); + ptr += mlen; + len -= mlen; + } + if (*s == '\r') s++; + if (*s == '\n') s++; + else if (s < send && (s+1 == send || s[1] == '\n')) + s += 2; /* possible checksum byte */ + } + RSTRING(str)->len = total; + ary_push(ary, str); + } + break; + + case '@': + s = str->ptr + len; + break; + + case 'X': + if (len > s - str->ptr) + ArgError("X outside of string"); + s -= len; + break; + + case 'x': + if (len > send - s) + ArgError("x outside of string"); + s += len; + break; + + default: + break; + } + } + + return ary; +} + +void +Init_pack() +{ + rb_define_method(cArray, "pack", pack_pack, 1); + rb_define_method(cString, "unpack", pack_unpack, 1); +} |