source: EcnlProtoTool/trunk/mrbgems/mruby-pack/src/pack.c@ 279

Last change on this file since 279 was 279, checked in by coas-nagasima, 7 years ago

ファイルを追加、更新。

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc
File size: 27.0 KB
Line 
1/*
2 ** pack.c - Array#pack, String#unpack
3 */
4
5#include "mruby.h"
6#include "error.h"
7#include "mruby/array.h"
8#include "mruby/class.h"
9#include "mruby/numeric.h"
10#include "mruby/string.h"
11#include "mruby/variable.h"
12
13#include <ctype.h>
14#include <errno.h>
15#include <limits.h>
16#include <stdio.h>
17#include <string.h>
18
19struct tmpl {
20 mrb_value str;
21 int idx;
22};
23
24enum {
25 PACK_DIR_CHAR, /* C */
26 PACK_DIR_SHORT, /* S */
27 PACK_DIR_LONG, /* L */
28 PACK_DIR_QUAD, /* Q */
29 //PACK_DIR_INT, /* i */
30 //PACK_DIR_VAX,
31 PACK_DIR_UTF8, /* U */
32 //PACK_DIR_BER,
33 PACK_DIR_DOUBLE, /* E */
34 PACK_DIR_FLOAT, /* f */
35 PACK_DIR_STR, /* A */
36 PACK_DIR_HEX, /* h */
37 PACK_DIR_BASE64, /* m */
38 PACK_DIR_NUL, /* x */
39 PACK_DIR_INVALID
40};
41
42enum {
43 PACK_TYPE_INTEGER,
44 PACK_TYPE_FLOAT,
45 PACK_TYPE_STRING,
46 PACK_TYPE_NONE
47};
48
49#define PACK_FLAG_s 0x00000001 /* native size ("_" "!") */
50#define PACK_FLAG_a 0x00000002 /* null padding ("a") */
51#define PACK_FLAG_Z 0x00000004 /* append nul char ("z") */
52#define PACK_FLAG_SIGNED 0x00000008 /* native size ("_" "!") */
53#define PACK_FLAG_GT 0x00000010 /* big endian (">") */
54#define PACK_FLAG_LT 0x00000020 /* little endian ("<") */
55#define PACK_FLAG_WIDTH 0x00000040 /* "count" is "width" */
56#define PACK_FLAG_LSB 0x00000080 /* LSB / low nibble first */
57#define PACK_FLAG_COUNT2 0x00000100 /* "count" is special... */
58#define PACK_FLAG_LITTLEENDIAN 0x00000200 /* little endian actually */
59
60#define PACK_BASE64_IGNORE 0xff
61#define PACK_BASE64_PADDING 0xfe
62
63static int littleendian = 0;
64
65const static unsigned char base64chars[] =
66 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
67static signed char base64_dec_tab[128];
68
69
70static int
71check_little_endian(void)
72{
73 unsigned int n = 1;
74 return (*(unsigned char *)&n == 1);
75}
76
77static unsigned int
78hex2int(unsigned char ch)
79{
80 if (ch >= '0' && ch <= '9')
81 return ch - '0';
82 else if (ch >= 'A' && ch <= 'F')
83 return 10 + (ch - 'A');
84 else if (ch >= 'a' && ch <= 'f')
85 return 10 + (ch - 'a');
86 else
87 return 0;
88}
89
90static void
91make_base64_dec_tab(void)
92{
93 int i;
94 memset(base64_dec_tab, PACK_BASE64_IGNORE, sizeof(base64_dec_tab));
95 for (i = 0; i < 26; i++)
96 base64_dec_tab['A' + i] = i;
97 for (i = 0; i < 26; i++)
98 base64_dec_tab['a' + i] = i + 26;
99 for (i = 0; i < 10; i++)
100 base64_dec_tab['0' + i] = i + 52;
101 base64_dec_tab['+'] = 62;
102 base64_dec_tab['/'] = 63;
103 base64_dec_tab['='] = PACK_BASE64_PADDING;
104}
105
106static mrb_value
107str_len_ensure(mrb_state *mrb, mrb_value str, int len)
108{
109 int n = RSTRING_LEN(str);
110 if (len > n) {
111 do {
112 n *= 2;
113 } while (len > n);
114 str = mrb_str_resize(mrb, str, n);
115 }
116 return str;
117}
118
119
120static int
121pack_c(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
122{
123 str = str_len_ensure(mrb, str, sidx + 1);
124 RSTRING_PTR(str)[sidx] = mrb_fixnum(o);
125 return 1;
126}
127
128static int
129unpack_c(mrb_state *mrb, const void *src, int srclen, mrb_value ary, unsigned int flags)
130{
131 if (flags & PACK_FLAG_SIGNED)
132 mrb_ary_push(mrb, ary, mrb_fixnum_value(*(signed char *)src));
133 else
134 mrb_ary_push(mrb, ary, mrb_fixnum_value(*(unsigned char *)src));
135 return 1;
136}
137
138static int
139pack_s(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
140{
141 unsigned short n;
142
143 str = str_len_ensure(mrb, str, sidx + 2);
144 n = mrb_fixnum(o);
145 if (flags & PACK_FLAG_LITTLEENDIAN) {
146 RSTRING_PTR(str)[sidx+0] = n % 256;
147 RSTRING_PTR(str)[sidx+1] = n / 256;
148 } else {
149 RSTRING_PTR(str)[sidx+0] = n / 256;
150 RSTRING_PTR(str)[sidx+1] = n % 256;
151 }
152 return 2;
153}
154
155static int
156unpack_s(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
157{
158 int n;
159
160 if (flags & PACK_FLAG_LITTLEENDIAN) {
161 n = src[1] * 256 + src[0];
162 } else {
163 n = src[0] * 256 + src[1];
164 }
165 if ((flags & PACK_FLAG_SIGNED) && (n >= 0x8000)) {
166 n -= 0x10000;
167 }
168 mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
169 return 2;
170}
171
172static int
173pack_l(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
174{
175 unsigned long n;
176 str = str_len_ensure(mrb, str, sidx + 4);
177 n = mrb_fixnum(o);
178 if (flags & PACK_FLAG_LITTLEENDIAN) {
179 RSTRING_PTR(str)[sidx+0] = n & 0xff;
180 RSTRING_PTR(str)[sidx+1] = n >> 8;
181 RSTRING_PTR(str)[sidx+2] = n >> 16;
182 RSTRING_PTR(str)[sidx+3] = n >> 24;
183 } else {
184 RSTRING_PTR(str)[sidx+0] = n >> 24;
185 RSTRING_PTR(str)[sidx+1] = n >> 16;
186 RSTRING_PTR(str)[sidx+2] = n >> 8;
187 RSTRING_PTR(str)[sidx+3] = n & 0xff;
188 }
189 return 4;
190}
191
192static int
193unpack_l(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
194{
195 char msg[60];
196 uint32_t ul;
197 mrb_int n;
198
199 if (flags & PACK_FLAG_LITTLEENDIAN) {
200 ul = (uint32_t)src[3] * 256*256*256;
201 ul += (uint32_t)src[2] *256*256;
202 ul += (uint32_t)src[1] *256;
203 ul += (uint32_t)src[0];
204 } else {
205 ul = (uint32_t)src[0] * 256*256*256;
206 ul += (uint32_t)src[1] *256*256;
207 ul += (uint32_t)src[2] *256;
208 ul += (uint32_t)src[3];
209 }
210 if (flags & PACK_FLAG_SIGNED) {
211 int32_t sl = ul;
212 if (!FIXABLE(sl)) {
213 snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %ld", (long)sl);
214 mrb_raise(mrb, E_RANGE_ERROR, msg);
215 }
216 n = sl;
217 } else {
218 if (!POSFIXABLE(ul)) {
219 snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %lu", (unsigned long)ul);
220 mrb_raise(mrb, E_RANGE_ERROR, msg);
221 }
222 n = ul;
223 }
224 mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
225 return 4;
226}
227
228static int
229pack_q(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
230{
231 unsigned long long n;
232 str = str_len_ensure(mrb, str, sidx + 8);
233 n = mrb_fixnum(o);
234 if (flags & PACK_FLAG_LITTLEENDIAN) {
235 RSTRING_PTR(str)[sidx+0] = n & 0xff;
236 RSTRING_PTR(str)[sidx+1] = n >> 8;
237 RSTRING_PTR(str)[sidx+2] = n >> 16;
238 RSTRING_PTR(str)[sidx+3] = n >> 24;
239 RSTRING_PTR(str)[sidx+4] = n >> 32;
240 RSTRING_PTR(str)[sidx+5] = n >> 40;
241 RSTRING_PTR(str)[sidx+6] = n >> 48;
242 RSTRING_PTR(str)[sidx+7] = n >> 56;
243 } else {
244 RSTRING_PTR(str)[sidx+0] = n >> 56;
245 RSTRING_PTR(str)[sidx+1] = n >> 48;
246 RSTRING_PTR(str)[sidx+2] = n >> 40;
247 RSTRING_PTR(str)[sidx+3] = n >> 32;
248 RSTRING_PTR(str)[sidx+4] = n >> 24;
249 RSTRING_PTR(str)[sidx+5] = n >> 16;
250 RSTRING_PTR(str)[sidx+6] = n >> 8;
251 RSTRING_PTR(str)[sidx+7] = n & 0xff;
252 }
253 return 8;
254}
255
256static int
257unpack_q(mrb_state *mrb, const unsigned char *src, int srclen, mrb_value ary, unsigned int flags)
258{
259 char msg[60];
260 uint64_t ull;
261 int i, pos, step;
262 mrb_int n;
263
264 if (flags & PACK_FLAG_LITTLEENDIAN) {
265 pos = 7;
266 step = -1;
267 } else {
268 pos = 0;
269 step = 1;
270 }
271 ull = 0;
272 for (i = 0; i < 8; i++) {
273 ull = ull * 256 + (uint64_t)src[pos];
274 pos += step;
275 }
276 if (flags & PACK_FLAG_SIGNED) {
277 int64_t sll = ull;
278 if (!FIXABLE(sll)) {
279 snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %lld", (long long)sll);
280 mrb_raise(mrb, E_RANGE_ERROR, msg);
281 }
282 n = sll;
283 } else {
284 if (!POSFIXABLE(ull)) {
285 snprintf(msg, sizeof(msg), "cannot unpack to Fixnum: %llu", (unsigned long long)ull);
286 mrb_raise(mrb, E_RANGE_ERROR, msg);
287 }
288 n = ull;
289 }
290 mrb_ary_push(mrb, ary, mrb_fixnum_value(n));
291 return 8;
292}
293
294static int
295pack_double(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
296{
297 int i;
298 double d;
299 uint8_t *buffer = (uint8_t *)&d;
300 str = str_len_ensure(mrb, str, sidx + 8);
301 d = mrb_float(o);
302
303 if (flags & PACK_FLAG_LITTLEENDIAN) {
304#ifdef MRB_ENDIAN_BIG
305 for (i = 0; i < 8; ++i) {
306 RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
307 }
308#else
309 memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
310#endif
311 } else {
312#ifdef MRB_ENDIAN_BIG
313 memcpy(RSTRING_PTR(str) + sidx, buffer, 8);
314#else
315 for (i = 0; i < 8; ++i) {
316 RSTRING_PTR(str)[sidx + i] = buffer[8 - i - 1];
317 }
318#endif
319 }
320
321 return 8;
322}
323
324static int
325unpack_double(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
326{
327 int i;
328 double d;
329 uint8_t *buffer = (uint8_t *)&d;
330
331 if (flags & PACK_FLAG_LITTLEENDIAN) {
332#ifdef MRB_ENDIAN_BIG
333 for (i = 0; i < 8; ++i) {
334 buffer[8 - i - 1] = src[i];
335 }
336#else
337 memcpy(buffer, src, 8);
338#endif
339 } else {
340#ifdef MRB_ENDIAN_BIG
341 memcpy(buffer, src, 8);
342#else
343 for (i = 0; i < 8; ++i) {
344 buffer[8 - i - 1] = src[i];
345 }
346#endif
347 }
348 mrb_ary_push(mrb, ary, mrb_float_value(mrb, d));
349
350 return 8;
351}
352
353static int
354pack_float(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, unsigned int flags)
355{
356 int i;
357 float f;
358 uint8_t *buffer = (uint8_t *)&f;
359 str = str_len_ensure(mrb, str, sidx + 4);
360 f = mrb_float(o);
361
362 if (flags & PACK_FLAG_LITTLEENDIAN) {
363#ifdef MRB_ENDIAN_BIG
364 for (i = 0; i < 4; ++i) {
365 RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
366 }
367#else
368 memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
369#endif
370 } else {
371#ifdef MRB_ENDIAN_BIG
372 memcpy(RSTRING_PTR(str) + sidx, buffer, 4);
373#else
374 for (i = 0; i < 4; ++i) {
375 RSTRING_PTR(str)[sidx + i] = buffer[4 - i - 1];
376 }
377#endif
378 }
379
380 return 4;
381}
382
383static int
384unpack_float(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
385{
386 int i;
387 float f;
388 uint8_t *buffer = (uint8_t *)&f;
389
390 if (flags & PACK_FLAG_LITTLEENDIAN) {
391#ifdef MRB_ENDIAN_BIG
392 for (i = 0; i < 4; ++i) {
393 buffer[4 - i - 1] = src[i];
394 }
395#else
396 memcpy(buffer, src, 4);
397#endif
398 } else {
399#ifdef MRB_ENDIAN_BIG
400 memcpy(buffer, src, 4);
401#else
402 for (i = 0; i < 4; ++i) {
403 buffer[4 - i - 1] = src[i];
404 }
405#endif
406 }
407 mrb_ary_push(mrb, ary, mrb_float_value(mrb, f));
408
409 return 4;
410}
411
412static int
413pack_utf8(mrb_state *mrb, mrb_value o, mrb_value str, mrb_int sidx, long count, unsigned int flags)
414{
415 char utf8[4];
416 int len;
417
418 unsigned long c = mrb_fixnum(o);
419
420 /* Unicode character */
421 /* from mruby-compiler gem */
422 if (c < 0x80) {
423 utf8[0] = (char)c;
424 len = 1;
425 }
426 else if (c < 0x800) {
427 utf8[0] = (char)(0xC0 | (c >> 6));
428 utf8[1] = (char)(0x80 | (c & 0x3F));
429 len = 2;
430 }
431 else if (c < 0x10000) {
432 utf8[0] = (char)(0xE0 | (c >> 12) );
433 utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F));
434 utf8[2] = (char)(0x80 | ( c & 0x3F));
435 len = 3;
436 }
437 else {
438 utf8[0] = (char)(0xF0 | (c >> 18) );
439 utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F));
440 utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F));
441 utf8[3] = (char)(0x80 | ( c & 0x3F));
442 len = 4;
443 }
444
445 str = str_len_ensure(mrb, str, sidx + len);
446 memcpy(RSTRING_PTR(str) + sidx, utf8, len);
447
448 return len;
449}
450
451static int
452pack_a(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
453{
454 int copylen, slen, padlen;
455 char *dptr, *dptr0, pad, *sptr;
456
457 sptr = RSTRING_PTR(src);
458 slen = RSTRING_LEN(src);
459
460 if ((flags & PACK_FLAG_a) || (flags & PACK_FLAG_Z))
461 pad = '\0';
462 else
463 pad = ' ';
464
465 if (count == 0) {
466 return 0;
467 } else if (count == -1) {
468 copylen = slen;
469 padlen = (flags & PACK_FLAG_Z) ? 1 : 0;
470 } else if (count < slen) {
471 copylen = count;
472 padlen = 0;
473 } else {
474 copylen = slen;
475 padlen = count - slen;
476 }
477
478 dst = str_len_ensure(mrb, dst, didx + copylen + padlen);
479 dptr0 = dptr = RSTRING_PTR(dst) + didx;
480 memcpy(dptr, sptr, copylen);
481 dptr += copylen;
482 while (padlen-- > 0) {
483 *dptr++ = pad;
484 }
485
486 return dptr - dptr0;
487}
488
489static int
490unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, unsigned int flags)
491{
492 mrb_value dst;
493 const char *cp, *sptr;
494 long copylen;
495
496 sptr = (const char *)src;
497 if (count != -1 && count < slen) {
498 slen = count;
499 }
500 copylen = slen;
501
502 if (flags & PACK_FLAG_Z) { /* "Z" */
503 if ((cp = (const char *)memchr(sptr, '\0', slen)) != NULL) {
504 copylen = cp - sptr;
505 if (count == -1) {
506 slen = copylen + 1;
507 }
508 }
509 } else if (!(flags & PACK_FLAG_a)) { /* "A" */
510 while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) {
511 copylen--;
512 }
513 }
514
515 dst = mrb_str_new(mrb, sptr, copylen);
516 mrb_ary_push(mrb, ary, dst);
517 return slen;
518}
519
520
521static int
522pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
523{
524 unsigned int a, ashift, b, bshift;
525 int slen;
526 char *dptr, *dptr0, *sptr;
527
528 sptr = RSTRING_PTR(src);
529 slen = RSTRING_LEN(src);
530
531 if (flags & PACK_FLAG_LSB) {
532 ashift = 0;
533 bshift = 4;
534 } else {
535 ashift = 4;
536 bshift = 0;
537 }
538
539 if (count == -1) {
540 count = slen;
541 } else if (slen > count) {
542 slen = count;
543 }
544
545 dst = str_len_ensure(mrb, dst, didx + count);
546 dptr = RSTRING_PTR(dst) + didx;
547
548 dptr0 = dptr;
549 for (; count > 0; count -= 2) {
550 a = b = 0;
551 if (slen > 0) {
552 a = hex2int(*sptr++);
553 slen--;
554 }
555 if (slen > 0) {
556 b = hex2int(*sptr++);
557 slen--;
558 }
559 *dptr++ = (a << ashift) + (b << bshift);
560 }
561
562 return dptr - dptr0;
563}
564
565static int
566unpack_h(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
567{
568 mrb_value dst;
569 int a, ashift, b, bshift;
570 const char *sptr, *sptr0;
571 char *dptr, *dptr0;
572 const char hexadecimal[] = "0123456789abcdef";
573
574 if (flags & PACK_FLAG_LSB) {
575 ashift = 0;
576 bshift = 4;
577 } else {
578 ashift = 4;
579 bshift = 0;
580 }
581
582 sptr = (const char *)src;
583
584 if (count == -1)
585 count = slen * 2;
586
587 dst = mrb_str_new(mrb, NULL, count);
588 dptr = RSTRING_PTR(dst);
589
590 sptr0 = sptr;
591 dptr0 = dptr;
592 while (slen > 0 && count > 0) {
593 a = (*sptr >> ashift) & 0x0f;
594 b = (*sptr >> bshift) & 0x0f;
595 sptr++;
596 slen--;
597
598 *dptr++ = hexadecimal[a];
599 count--;
600
601 if (count > 0) {
602 *dptr++ = hexadecimal[b];
603 count--;
604 }
605 }
606
607 dst = mrb_str_resize(mrb, dst, dptr - dptr0);
608 mrb_ary_push(mrb, ary, dst);
609 return sptr - sptr0;
610}
611
612
613static int
614pack_m(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
615{
616 mrb_int dstlen;
617 unsigned long l;
618 int column, srclen;
619 char *srcptr, *dstptr, *dstptr0;
620
621 srcptr = RSTRING_PTR(src);
622 srclen = RSTRING_LEN(src);
623
624 if (srclen == 0) /* easy case */
625 return 0;
626
627 if (count != 0 && count < 3) { /* -1, 1 or 2 */
628 count = 45;
629 } else if (count >= 3) {
630 count -= count % 3;
631 }
632
633 dstlen = srclen / 3 * 4;
634 if (count > 0) {
635 dstlen += (srclen / count) + ((srclen % count) == 0 ? 0 : 1);
636 }
637 dst = str_len_ensure(mrb, dst, didx + dstlen);
638 dstptr = RSTRING_PTR(dst) + didx;
639
640 dstptr0 = dstptr;
641 for (column = 3; srclen >= 3; srclen -= 3, column += 3) {
642 l = (unsigned char)*srcptr++ << 16;
643 l += (unsigned char)*srcptr++ << 8;
644 l += (unsigned char)*srcptr++;
645
646 *dstptr++ = base64chars[(l >> 18) & 0x3f];
647 *dstptr++ = base64chars[(l >> 12) & 0x3f];
648 *dstptr++ = base64chars[(l >> 6) & 0x3f];
649 *dstptr++ = base64chars[ l & 0x3f];
650
651 if (column == count) {
652 *dstptr++ = '\n';
653 column = 0;
654 }
655 }
656 if (srclen == 1) {
657 l = (unsigned char)*srcptr++ << 16;
658 *dstptr++ = base64chars[(l >> 18) & 0x3f];
659 *dstptr++ = base64chars[(l >> 12) & 0x3f];
660 *dstptr++ = '=';
661 *dstptr++ = '=';
662 column += 3;
663 } else if (srclen == 2) {
664 l = (unsigned char)*srcptr++ << 16;
665 l += (unsigned char)*srcptr++ << 8;
666 *dstptr++ = base64chars[(l >> 18) & 0x3f];
667 *dstptr++ = base64chars[(l >> 12) & 0x3f];
668 *dstptr++ = base64chars[(l >> 6) & 0x3f];
669 *dstptr++ = '=';
670 column += 3;
671 }
672 if (column > 0 && count > 0) {
673 *dstptr++ = '\n';
674 }
675
676 return dstptr - dstptr0;
677}
678
679static int
680unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int flags)
681{
682 mrb_value dst;
683 int dlen;
684 unsigned long l;
685 int i, padding;
686 unsigned char c, ch[4];
687 const char *sptr, *sptr0;
688 char *dptr, *dptr0;
689
690 sptr0 = sptr = (const char *)src;
691
692 dlen = slen / 4 * 3; /* an estimated value - may be shorter */
693 dst = mrb_str_new(mrb, NULL, dlen);
694 dptr0 = dptr = RSTRING_PTR(dst);
695
696 padding = 0;
697 while (slen >= 4) {
698 for (i = 0; i < 4; i++) {
699 do {
700 if (slen-- == 0)
701 goto done;
702 c = *sptr++;
703 if (c >= sizeof(base64_dec_tab))
704 continue;
705 ch[i] = base64_dec_tab[c];
706 if (ch[i] == PACK_BASE64_PADDING) {
707 ch[i] = 0;
708 padding++;
709 }
710 } while (ch[i] == PACK_BASE64_IGNORE);
711 }
712
713 l = (ch[0] << 18) + (ch[1] << 12) + (ch[2] << 6) + ch[3];
714
715 if (padding == 0) {
716 *dptr++ = (l >> 16) & 0xff;
717 *dptr++ = (l >> 8) & 0xff;
718 *dptr++ = l & 0xff;
719 } else if (padding == 1) {
720 *dptr++ = (l >> 16) & 0xff;
721 *dptr++ = (l >> 8) & 0xff;
722 break;
723 } else {
724 *dptr++ = (l >> 16) & 0xff;
725 break;
726 }
727 }
728
729done:
730 dst = mrb_str_resize(mrb, dst, dptr - dptr0);
731 mrb_ary_push(mrb, ary, dst);
732 return sptr - sptr0;
733}
734
735static int
736pack_x(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
737{
738 long i;
739
740 dst = str_len_ensure(mrb, dst, didx + count);
741 for (i = 0; i < count; i++) {
742 RSTRING_PTR(dst)[didx] = '\0';
743 }
744 return count;
745}
746
747static int
748unpack_x(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
749{
750 if (slen < count) {
751 mrb_raise(mrb, E_ARGUMENT_ERROR, "x outside of string");
752 }
753 return count;
754}
755
756static void
757prepare_tmpl(mrb_state *mrb, struct tmpl *tmpl)
758{
759 mrb_get_args(mrb, "S", &tmpl->str);
760 tmpl->idx = 0;
761}
762
763static int
764has_tmpl(const struct tmpl *tmpl)
765{
766 return (tmpl->idx < RSTRING_LEN(tmpl->str));
767}
768
769static void
770read_tmpl(mrb_state *mrb, struct tmpl *tmpl, int *dirp, int *typep, int *sizep, long *countp, unsigned int *flagsp)
771{
772 int ch, dir, t, tlen, type;
773 int size = 0;
774 long count = 1;
775 unsigned int flags = 0;
776 const char *tptr;
777
778 tptr = RSTRING_PTR(tmpl->str);
779 tlen = RSTRING_LEN(tmpl->str);
780
781 t = tptr[tmpl->idx++];
782alias:
783 switch (t) {
784 case 'A':
785 dir = PACK_DIR_STR;
786 type = PACK_TYPE_STRING;
787 flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
788 break;
789 case 'a':
790 dir = PACK_DIR_STR;
791 type = PACK_TYPE_STRING;
792 flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_a;
793 break;
794 case 'C':
795 dir = PACK_DIR_CHAR;
796 type = PACK_TYPE_INTEGER;
797 size = 1;
798 break;
799 case 'c':
800 dir = PACK_DIR_CHAR;
801 type = PACK_TYPE_INTEGER;
802 size = 1;
803 flags |= PACK_FLAG_SIGNED;
804 break;
805 case 'D': case 'd':
806 dir = PACK_DIR_DOUBLE;
807 type = PACK_TYPE_FLOAT;
808 size = 8;
809 flags |= PACK_FLAG_SIGNED;
810 break;
811 case 'F': case 'f':
812 dir = PACK_DIR_FLOAT;
813 type = PACK_TYPE_FLOAT;
814 size = 4;
815 flags |= PACK_FLAG_SIGNED;
816 break;
817 case 'E':
818 dir = PACK_DIR_DOUBLE;
819 type = PACK_TYPE_FLOAT;
820 size = 8;
821 flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT;
822 break;
823 case 'e':
824 dir = PACK_DIR_FLOAT;
825 type = PACK_TYPE_FLOAT;
826 size = 4;
827 flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT;
828 break;
829 case 'G':
830 dir = PACK_DIR_DOUBLE;
831 type = PACK_TYPE_FLOAT;
832 size = 8;
833 flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT;
834 break;
835 case 'g':
836 dir = PACK_DIR_FLOAT;
837 type = PACK_TYPE_FLOAT;
838 size = 4;
839 flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT;
840 break;
841 case 'H':
842 dir = PACK_DIR_HEX;
843 type = PACK_TYPE_STRING;
844 flags |= PACK_FLAG_COUNT2;
845 break;
846 case 'h':
847 dir = PACK_DIR_HEX;
848 type = PACK_TYPE_STRING;
849 flags |= PACK_FLAG_COUNT2 | PACK_FLAG_LSB;
850 break;
851 case 'I':
852 switch (sizeof(int)) {
853 case 2: t = 'S'; goto alias;
854 case 4: t = 'L'; goto alias;
855 case 8: t = 'Q'; goto alias;
856 default:
857 mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
858 }
859 break;
860 case 'i':
861 switch (sizeof(int)) {
862 case 2: t = 's'; goto alias;
863 case 4: t = 'l'; goto alias;
864 case 8: t = 'q'; goto alias;
865 default:
866 mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
867 }
868 break;
869 case 'L':
870 dir = PACK_DIR_LONG;
871 type = PACK_TYPE_INTEGER;
872 size = 4;
873 break;
874 case 'l':
875 dir = PACK_DIR_LONG;
876 type = PACK_TYPE_INTEGER;
877 size = 4;
878 flags |= PACK_FLAG_SIGNED;
879 break;
880 case 'm':
881 dir = PACK_DIR_BASE64;
882 type = PACK_TYPE_STRING;
883 flags |= PACK_FLAG_WIDTH;
884 break;
885 case 'N': /* = "L>" */
886 dir = PACK_DIR_LONG;
887 type = PACK_TYPE_INTEGER;
888 size = 4;
889 flags |= PACK_FLAG_GT;
890 break;
891 case 'n': /* = "S>" */
892 dir = PACK_DIR_SHORT;
893 type = PACK_TYPE_INTEGER;
894 size = 2;
895 flags |= PACK_FLAG_GT;
896 break;
897 case 'Q':
898 dir = PACK_DIR_QUAD;
899 type = PACK_TYPE_INTEGER;
900 size = 8;
901 break;
902 case 'q':
903 dir = PACK_DIR_QUAD;
904 type = PACK_TYPE_INTEGER;
905 size = 8;
906 flags |= PACK_FLAG_SIGNED;
907 break;
908 case 'S':
909 dir = PACK_DIR_SHORT;
910 type = PACK_TYPE_INTEGER;
911 size = 2;
912 break;
913 case 's':
914 dir = PACK_DIR_SHORT;
915 type = PACK_TYPE_INTEGER;
916 size = 2;
917 flags |= PACK_FLAG_SIGNED;
918 break;
919 case 'U':
920 dir = PACK_DIR_UTF8;
921 type = PACK_TYPE_INTEGER;
922 break;
923 case 'V': /* = "L<" */
924 dir = PACK_DIR_LONG;
925 type = PACK_TYPE_INTEGER;
926 size = 4;
927 flags |= PACK_FLAG_LT;
928 break;
929 case 'v': /* = "S<" */
930 dir = PACK_DIR_SHORT;
931 type = PACK_TYPE_INTEGER;
932 size = 2;
933 flags |= PACK_FLAG_LT;
934 break;
935 case 'x':
936 dir = PACK_DIR_NUL;
937 type = PACK_TYPE_NONE;
938 break;
939 case 'Z':
940 dir = PACK_DIR_STR;
941 type = PACK_TYPE_STRING;
942 flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_Z;
943 break;
944 default:
945 dir = PACK_DIR_INVALID;
946 type = PACK_TYPE_NONE;
947 break;
948 }
949
950 /* read suffix [0-9*_!<>] */
951 while (tmpl->idx < tlen) {
952 ch = tptr[tmpl->idx++];
953 if (isdigit(ch)) {
954 count = ch - '0';
955 while (tmpl->idx < tlen && isdigit(tptr[tmpl->idx])) {
956 count = count * 10 + (tptr[tmpl->idx++] - '0');
957 }
958 continue; /* special case */
959 } else if (ch == '*') {
960 count = -1;
961 } else if (ch == '_' || ch == '!' || ch == '<' || ch == '>') {
962 if (strchr("sSiIlLqQ", t) == NULL) {
963 char ch_str = ch;
964 mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%S' allowed only after types sSiIlLqQ", mrb_str_new(mrb, &ch_str, 1));
965 }
966 if (ch == '_' || ch == '!') {
967 flags |= PACK_FLAG_s;
968 } else if (ch == '<') {
969 flags |= PACK_FLAG_LT;
970 } else if (ch == '>') {
971 flags |= PACK_FLAG_GT;
972 }
973 } else {
974 tmpl->idx--;
975 break;
976 }
977 }
978
979 if ((flags & PACK_FLAG_LT) || (!(flags & PACK_FLAG_GT) && littleendian)) {
980 flags |= PACK_FLAG_LITTLEENDIAN;
981 }
982
983 *dirp = dir;
984 *typep = type;
985 *sizep = size;
986 *countp = count;
987 *flagsp = flags;
988}
989
990static mrb_value
991mrb_pack_pack(mrb_state *mrb, mrb_value ary)
992{
993 mrb_value o, result;
994 mrb_int aidx;
995 struct tmpl tmpl;
996 long count;
997 unsigned int flags;
998 int dir, ridx, size, type;
999
1000 prepare_tmpl(mrb, &tmpl);
1001
1002 result = mrb_str_new(mrb, NULL, 128); /* allocate initial buffer */
1003 aidx = 0;
1004 ridx = 0;
1005 while (has_tmpl(&tmpl)) {
1006 read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags);
1007
1008 if (dir == PACK_DIR_INVALID)
1009 continue;
1010 else if (dir == PACK_DIR_NUL) {
1011 ridx += pack_x(mrb, mrb_nil_value(), result, ridx, count, flags);
1012 continue;
1013 }
1014
1015 for (; aidx < RARRAY_LEN(ary); aidx++) {
1016 if (count == 0 && !(flags & PACK_FLAG_WIDTH))
1017 break;
1018
1019 o = mrb_ary_ref(mrb, ary, aidx);
1020 if (type == PACK_TYPE_INTEGER) {
1021 o = mrb_to_int(mrb, o);
1022 } else if (type == PACK_TYPE_FLOAT) {
1023 if (!mrb_float_p(o)) {
1024 o = mrb_funcall(mrb, o, "to_f", 0);
1025 }
1026 } else if (type == PACK_TYPE_STRING) {
1027 if (!mrb_string_p(o)) {
1028 mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
1029 }
1030 }
1031
1032 switch (dir) {
1033 case PACK_DIR_CHAR:
1034 ridx += pack_c(mrb, o, result, ridx, flags);
1035 break;
1036 case PACK_DIR_SHORT:
1037 ridx += pack_s(mrb, o, result, ridx, flags);
1038 break;
1039 case PACK_DIR_LONG:
1040 ridx += pack_l(mrb, o, result, ridx, flags);
1041 break;
1042 case PACK_DIR_QUAD:
1043 ridx += pack_q(mrb, o, result, ridx, flags);
1044 break;
1045 case PACK_DIR_BASE64:
1046 ridx += pack_m(mrb, o, result, ridx, count, flags);
1047 break;
1048 case PACK_DIR_HEX:
1049 ridx += pack_h(mrb, o, result, ridx, count, flags);
1050 break;
1051 case PACK_DIR_STR:
1052 ridx += pack_a(mrb, o, result, ridx, count, flags);
1053 break;
1054 case PACK_DIR_DOUBLE:
1055 ridx += pack_double(mrb, o, result, ridx, flags);
1056 break;
1057 case PACK_DIR_FLOAT:
1058 ridx += pack_float(mrb, o, result, ridx, flags);
1059 break;
1060 case PACK_DIR_UTF8:
1061 ridx += pack_utf8(mrb, o, result, ridx, count, flags);
1062 break;
1063 default:
1064 break;
1065 }
1066 if (dir == PACK_DIR_STR) { /* always consumes 1 entry */
1067 aidx++;
1068 break;
1069 }
1070 if (count > 0) {
1071 count--;
1072 }
1073 }
1074 }
1075
1076 mrb_str_resize(mrb, result, ridx);
1077 return result;
1078}
1079
1080static mrb_value
1081mrb_pack_unpack(mrb_state *mrb, mrb_value str)
1082{
1083 mrb_value result;
1084 struct tmpl tmpl;
1085 long count;
1086 unsigned int flags;
1087 int dir, size, srcidx, srclen, type;
1088 const unsigned char *sptr;
1089
1090 prepare_tmpl(mrb, &tmpl);
1091
1092 srcidx = 0;
1093 srclen = RSTRING_LEN(str);
1094
1095 result = mrb_ary_new(mrb);
1096 while (has_tmpl(&tmpl)) {
1097 read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags);
1098
1099 if (dir == PACK_DIR_INVALID)
1100 continue;
1101 else if (dir == PACK_DIR_NUL) {
1102 srcidx += unpack_x(mrb, sptr, srclen - srcidx, result, count, flags);
1103 continue;
1104 }
1105
1106 if (flags & PACK_FLAG_COUNT2) {
1107 sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx;
1108 switch (dir) {
1109 case PACK_DIR_HEX:
1110 srcidx += unpack_h(mrb, sptr, srclen - srcidx, result, count, flags);
1111 break;
1112 case PACK_DIR_STR:
1113 srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags);
1114 break;
1115 }
1116 continue;
1117 }
1118
1119 while (count != 0) {
1120 if (srclen - srcidx < size) {
1121 while (count-- > 0) {
1122 mrb_ary_push(mrb, result, mrb_nil_value());
1123 }
1124 break;
1125 }
1126
1127 sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx;
1128 switch (dir) {
1129 case PACK_DIR_CHAR:
1130 srcidx += unpack_c(mrb, sptr, srclen - srcidx, result, flags);
1131 break;
1132 case PACK_DIR_SHORT:
1133 srcidx += unpack_s(mrb, sptr, srclen - srcidx, result, flags);
1134 break;
1135 case PACK_DIR_LONG:
1136 srcidx += unpack_l(mrb, sptr, srclen - srcidx, result, flags);
1137 break;
1138 case PACK_DIR_QUAD:
1139 srcidx += unpack_q(mrb, sptr, srclen - srcidx, result, flags);
1140 break;
1141 case PACK_DIR_BASE64:
1142 srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
1143 break;
1144 case PACK_DIR_FLOAT:
1145 srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags);
1146 break;
1147 case PACK_DIR_DOUBLE:
1148 srcidx += unpack_double(mrb, sptr, srclen - srcidx, result, flags);
1149 break;
1150 }
1151 if (count > 0) {
1152 count--;
1153 }
1154 }
1155 }
1156
1157 return result;
1158}
1159
1160void
1161mrb_mruby_pack_gem_init(mrb_state *mrb)
1162{
1163 littleendian = check_little_endian();
1164 make_base64_dec_tab();
1165
1166 mrb_define_method(mrb, mrb->array_class, "pack", mrb_pack_pack, MRB_ARGS_REQ(1));
1167 mrb_define_method(mrb, mrb->string_class, "unpack", mrb_pack_unpack, MRB_ARGS_REQ(1));
1168}
1169
1170void
1171mrb_mruby_pack_gem_final(mrb_state *mrb)
1172{
1173}
Note: See TracBrowser for help on using the repository browser.