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

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

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 29.1 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 unsigned long c = 0;
418
419 if (mrb_float_p(o)) {
420 goto range_error;
421 }
422 c = mrb_fixnum(o);
423
424 /* Unicode character */
425 /* from mruby-compiler gem */
426 if (c < 0x80) {
427 utf8[0] = (char)c;
428 len = 1;
429 }
430 else if (c < 0x800) {
431 utf8[0] = (char)(0xC0 | (c >> 6));
432 utf8[1] = (char)(0x80 | (c & 0x3F));
433 len = 2;
434 }
435 else if (c < 0x10000) {
436 utf8[0] = (char)(0xE0 | (c >> 12) );
437 utf8[1] = (char)(0x80 | ((c >> 6) & 0x3F));
438 utf8[2] = (char)(0x80 | ( c & 0x3F));
439 len = 3;
440 }
441 else if (c < 0x200000) {
442 utf8[0] = (char)(0xF0 | (c >> 18) );
443 utf8[1] = (char)(0x80 | ((c >> 12) & 0x3F));
444 utf8[2] = (char)(0x80 | ((c >> 6) & 0x3F));
445 utf8[3] = (char)(0x80 | ( c & 0x3F));
446 len = 4;
447 }
448 else {
449range_error:
450 mrb_raise(mrb, E_RANGE_ERROR, "pack(U): value out of range");
451 }
452
453 str = str_len_ensure(mrb, str, sidx + len);
454 memcpy(RSTRING_PTR(str) + sidx, utf8, len);
455
456 return len;
457}
458
459static const unsigned long utf8_limits[] = {
460 0x0, /* 1 */
461 0x80, /* 2 */
462 0x800, /* 3 */
463 0x10000, /* 4 */
464 0x200000, /* 5 */
465 0x4000000, /* 6 */
466 0x80000000, /* 7 */
467};
468
469static unsigned long
470utf8_to_uv(mrb_state *mrb, const char *p, long *lenp)
471{
472 int c = *p++ & 0xff;
473 unsigned long uv = c;
474 long n;
475
476 if (!(uv & 0x80)) {
477 *lenp = 1;
478 return uv;
479 }
480 if (!(uv & 0x40)) {
481 *lenp = 1;
482 mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
483 }
484
485 if (!(uv & 0x20)) { n = 2; uv &= 0x1f; }
486 else if (!(uv & 0x10)) { n = 3; uv &= 0x0f; }
487 else if (!(uv & 0x08)) { n = 4; uv &= 0x07; }
488 else if (!(uv & 0x04)) { n = 5; uv &= 0x03; }
489 else if (!(uv & 0x02)) { n = 6; uv &= 0x01; }
490 else {
491 *lenp = 1;
492 mrb_raise(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
493 }
494 if (n > *lenp) {
495 mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character (expected %S bytes, given %S bytes)",
496 mrb_fixnum_value(n), mrb_fixnum_value(*lenp));
497 }
498 *lenp = n--;
499 if (n != 0) {
500 while (n--) {
501 c = *p++ & 0xff;
502 if ((c & 0xc0) != 0x80) {
503 *lenp -= n + 1;
504 mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed UTF-8 character");
505 }
506 else {
507 c &= 0x3f;
508 uv = uv << 6 | c;
509 }
510 }
511 }
512 n = *lenp - 1;
513 if (uv < utf8_limits[n]) {
514 mrb_raisef(mrb, E_ARGUMENT_ERROR, "redundant UTF-8 sequence");
515 }
516 return uv;
517}
518
519static int
520unpack_utf8(mrb_state *mrb, const unsigned char * src, int srclen, mrb_value ary, unsigned int flags)
521{
522 unsigned long uv;
523 long lenp = srclen;
524
525 if (srclen == 0) {
526 return 1;
527 }
528 uv = utf8_to_uv(mrb, (const char *)src, &lenp);
529 mrb_ary_push(mrb, ary, mrb_fixnum_value((mrb_int)uv));
530 return (int)lenp;
531}
532
533static int
534pack_a(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
535{
536 int copylen, slen, padlen;
537 char *dptr, *dptr0, pad, *sptr;
538
539 sptr = RSTRING_PTR(src);
540 slen = RSTRING_LEN(src);
541
542 if ((flags & PACK_FLAG_a) || (flags & PACK_FLAG_Z))
543 pad = '\0';
544 else
545 pad = ' ';
546
547 if (count == 0) {
548 return 0;
549 } else if (count == -1) {
550 copylen = slen;
551 padlen = (flags & PACK_FLAG_Z) ? 1 : 0;
552 } else if (count < slen) {
553 copylen = count;
554 padlen = 0;
555 } else {
556 copylen = slen;
557 padlen = count - slen;
558 }
559
560 dst = str_len_ensure(mrb, dst, didx + copylen + padlen);
561 dptr0 = dptr = RSTRING_PTR(dst) + didx;
562 memcpy(dptr, sptr, copylen);
563 dptr += copylen;
564 while (padlen-- > 0) {
565 *dptr++ = pad;
566 }
567
568 return dptr - dptr0;
569}
570
571static int
572unpack_a(mrb_state *mrb, const void *src, int slen, mrb_value ary, long count, unsigned int flags)
573{
574 mrb_value dst;
575 const char *cp, *sptr;
576 long copylen;
577
578 sptr = (const char *)src;
579 if (count != -1 && count < slen) {
580 slen = count;
581 }
582 copylen = slen;
583
584 if (flags & PACK_FLAG_Z) { /* "Z" */
585 if ((cp = (const char *)memchr(sptr, '\0', slen)) != NULL) {
586 copylen = cp - sptr;
587 if (count == -1) {
588 slen = copylen + 1;
589 }
590 }
591 } else if (!(flags & PACK_FLAG_a)) { /* "A" */
592 while (copylen > 0 && (sptr[copylen - 1] == '\0' || isspace(sptr[copylen - 1]))) {
593 copylen--;
594 }
595 }
596
597 dst = mrb_str_new(mrb, sptr, copylen);
598 mrb_ary_push(mrb, ary, dst);
599 return slen;
600}
601
602
603static int
604pack_h(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
605{
606 unsigned int a, ashift, b, bshift;
607 int slen;
608 char *dptr, *dptr0, *sptr;
609
610 sptr = RSTRING_PTR(src);
611 slen = RSTRING_LEN(src);
612
613 if (flags & PACK_FLAG_LSB) {
614 ashift = 0;
615 bshift = 4;
616 } else {
617 ashift = 4;
618 bshift = 0;
619 }
620
621 if (count == -1) {
622 count = slen;
623 } else if (slen > count) {
624 slen = count;
625 }
626
627 dst = str_len_ensure(mrb, dst, didx + count);
628 dptr = RSTRING_PTR(dst) + didx;
629
630 dptr0 = dptr;
631 for (; count > 0; count -= 2) {
632 a = b = 0;
633 if (slen > 0) {
634 a = hex2int(*sptr++);
635 slen--;
636 }
637 if (slen > 0) {
638 b = hex2int(*sptr++);
639 slen--;
640 }
641 *dptr++ = (a << ashift) + (b << bshift);
642 }
643
644 return dptr - dptr0;
645}
646
647static int
648unpack_h(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
649{
650 mrb_value dst;
651 int a, ashift, b, bshift;
652 const char *sptr, *sptr0;
653 char *dptr, *dptr0;
654 const char hexadecimal[] = "0123456789abcdef";
655
656 if (flags & PACK_FLAG_LSB) {
657 ashift = 0;
658 bshift = 4;
659 } else {
660 ashift = 4;
661 bshift = 0;
662 }
663
664 sptr = (const char *)src;
665
666 if (count == -1)
667 count = slen * 2;
668
669 dst = mrb_str_new(mrb, NULL, count);
670 dptr = RSTRING_PTR(dst);
671
672 sptr0 = sptr;
673 dptr0 = dptr;
674 while (slen > 0 && count > 0) {
675 a = (*sptr >> ashift) & 0x0f;
676 b = (*sptr >> bshift) & 0x0f;
677 sptr++;
678 slen--;
679
680 *dptr++ = hexadecimal[a];
681 count--;
682
683 if (count > 0) {
684 *dptr++ = hexadecimal[b];
685 count--;
686 }
687 }
688
689 dst = mrb_str_resize(mrb, dst, dptr - dptr0);
690 mrb_ary_push(mrb, ary, dst);
691 return sptr - sptr0;
692}
693
694
695static int
696pack_m(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
697{
698 mrb_int dstlen;
699 unsigned long l;
700 int column, srclen;
701 char *srcptr, *dstptr, *dstptr0;
702
703 srcptr = RSTRING_PTR(src);
704 srclen = RSTRING_LEN(src);
705
706 if (srclen == 0) /* easy case */
707 return 0;
708
709 if (count != 0 && count < 3) { /* -1, 1 or 2 */
710 count = 45;
711 } else if (count >= 3) {
712 count -= count % 3;
713 }
714
715 dstlen = srclen / 3 * 4;
716 if (count > 0) {
717 dstlen += (srclen / count) + ((srclen % count) == 0 ? 0 : 1);
718 }
719 dst = str_len_ensure(mrb, dst, didx + dstlen);
720 dstptr = RSTRING_PTR(dst) + didx;
721
722 dstptr0 = dstptr;
723 for (column = 3; srclen >= 3; srclen -= 3, column += 3) {
724 l = (unsigned char)*srcptr++ << 16;
725 l += (unsigned char)*srcptr++ << 8;
726 l += (unsigned char)*srcptr++;
727
728 *dstptr++ = base64chars[(l >> 18) & 0x3f];
729 *dstptr++ = base64chars[(l >> 12) & 0x3f];
730 *dstptr++ = base64chars[(l >> 6) & 0x3f];
731 *dstptr++ = base64chars[ l & 0x3f];
732
733 if (column == count) {
734 *dstptr++ = '\n';
735 column = 0;
736 }
737 }
738 if (srclen == 1) {
739 l = (unsigned char)*srcptr++ << 16;
740 *dstptr++ = base64chars[(l >> 18) & 0x3f];
741 *dstptr++ = base64chars[(l >> 12) & 0x3f];
742 *dstptr++ = '=';
743 *dstptr++ = '=';
744 column += 3;
745 } else if (srclen == 2) {
746 l = (unsigned char)*srcptr++ << 16;
747 l += (unsigned char)*srcptr++ << 8;
748 *dstptr++ = base64chars[(l >> 18) & 0x3f];
749 *dstptr++ = base64chars[(l >> 12) & 0x3f];
750 *dstptr++ = base64chars[(l >> 6) & 0x3f];
751 *dstptr++ = '=';
752 column += 3;
753 }
754 if (column > 0 && count > 0) {
755 *dstptr++ = '\n';
756 }
757
758 return dstptr - dstptr0;
759}
760
761static int
762unpack_m(mrb_state *mrb, const void *src, int slen, mrb_value ary, unsigned int flags)
763{
764 mrb_value dst;
765 int dlen;
766 unsigned long l;
767 int i, padding;
768 unsigned char c, ch[4];
769 const char *sptr, *sptr0;
770 char *dptr, *dptr0;
771
772 sptr0 = sptr = (const char *)src;
773
774 dlen = slen / 4 * 3; /* an estimated value - may be shorter */
775 dst = mrb_str_new(mrb, NULL, dlen);
776 dptr0 = dptr = RSTRING_PTR(dst);
777
778 padding = 0;
779 while (slen >= 4) {
780 for (i = 0; i < 4; i++) {
781 do {
782 if (slen-- == 0)
783 goto done;
784 c = *sptr++;
785 if (c >= sizeof(base64_dec_tab))
786 continue;
787 ch[i] = base64_dec_tab[c];
788 if (ch[i] == PACK_BASE64_PADDING) {
789 ch[i] = 0;
790 padding++;
791 }
792 } while (ch[i] == PACK_BASE64_IGNORE);
793 }
794
795 l = (ch[0] << 18) + (ch[1] << 12) + (ch[2] << 6) + ch[3];
796
797 if (padding == 0) {
798 *dptr++ = (l >> 16) & 0xff;
799 *dptr++ = (l >> 8) & 0xff;
800 *dptr++ = l & 0xff;
801 } else if (padding == 1) {
802 *dptr++ = (l >> 16) & 0xff;
803 *dptr++ = (l >> 8) & 0xff;
804 break;
805 } else {
806 *dptr++ = (l >> 16) & 0xff;
807 break;
808 }
809 }
810
811done:
812 dst = mrb_str_resize(mrb, dst, dptr - dptr0);
813 mrb_ary_push(mrb, ary, dst);
814 return sptr - sptr0;
815}
816
817static int
818pack_x(mrb_state *mrb, mrb_value src, mrb_value dst, mrb_int didx, long count, unsigned int flags)
819{
820 long i;
821
822 dst = str_len_ensure(mrb, dst, didx + count);
823 for (i = 0; i < count; i++) {
824 RSTRING_PTR(dst)[didx] = '\0';
825 }
826 return count;
827}
828
829static int
830unpack_x(mrb_state *mrb, const void *src, int slen, mrb_value ary, int count, unsigned int flags)
831{
832 if (slen < count) {
833 mrb_raise(mrb, E_ARGUMENT_ERROR, "x outside of string");
834 }
835 return count;
836}
837
838static void
839prepare_tmpl(mrb_state *mrb, struct tmpl *tmpl)
840{
841 mrb_get_args(mrb, "S", &tmpl->str);
842 tmpl->idx = 0;
843}
844
845static int
846has_tmpl(const struct tmpl *tmpl)
847{
848 return (tmpl->idx < RSTRING_LEN(tmpl->str));
849}
850
851static void
852read_tmpl(mrb_state *mrb, struct tmpl *tmpl, int *dirp, int *typep, int *sizep, long *countp, unsigned int *flagsp)
853{
854 int ch, dir, t, tlen, type;
855 int size = 0;
856 long count = 1;
857 unsigned int flags = 0;
858 const char *tptr;
859
860 tptr = RSTRING_PTR(tmpl->str);
861 tlen = RSTRING_LEN(tmpl->str);
862
863 t = tptr[tmpl->idx++];
864alias:
865 switch (t) {
866 case 'A':
867 dir = PACK_DIR_STR;
868 type = PACK_TYPE_STRING;
869 flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2;
870 break;
871 case 'a':
872 dir = PACK_DIR_STR;
873 type = PACK_TYPE_STRING;
874 flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_a;
875 break;
876 case 'C':
877 dir = PACK_DIR_CHAR;
878 type = PACK_TYPE_INTEGER;
879 size = 1;
880 break;
881 case 'c':
882 dir = PACK_DIR_CHAR;
883 type = PACK_TYPE_INTEGER;
884 size = 1;
885 flags |= PACK_FLAG_SIGNED;
886 break;
887 case 'D': case 'd':
888 dir = PACK_DIR_DOUBLE;
889 type = PACK_TYPE_FLOAT;
890 size = 8;
891 flags |= PACK_FLAG_SIGNED;
892 break;
893 case 'F': case 'f':
894 dir = PACK_DIR_FLOAT;
895 type = PACK_TYPE_FLOAT;
896 size = 4;
897 flags |= PACK_FLAG_SIGNED;
898 break;
899 case 'E':
900 dir = PACK_DIR_DOUBLE;
901 type = PACK_TYPE_FLOAT;
902 size = 8;
903 flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT;
904 break;
905 case 'e':
906 dir = PACK_DIR_FLOAT;
907 type = PACK_TYPE_FLOAT;
908 size = 4;
909 flags |= PACK_FLAG_SIGNED | PACK_FLAG_LT;
910 break;
911 case 'G':
912 dir = PACK_DIR_DOUBLE;
913 type = PACK_TYPE_FLOAT;
914 size = 8;
915 flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT;
916 break;
917 case 'g':
918 dir = PACK_DIR_FLOAT;
919 type = PACK_TYPE_FLOAT;
920 size = 4;
921 flags |= PACK_FLAG_SIGNED | PACK_FLAG_GT;
922 break;
923 case 'H':
924 dir = PACK_DIR_HEX;
925 type = PACK_TYPE_STRING;
926 flags |= PACK_FLAG_COUNT2;
927 break;
928 case 'h':
929 dir = PACK_DIR_HEX;
930 type = PACK_TYPE_STRING;
931 flags |= PACK_FLAG_COUNT2 | PACK_FLAG_LSB;
932 break;
933 case 'I':
934 switch (sizeof(int)) {
935 case 2: t = 'S'; goto alias;
936 case 4: t = 'L'; goto alias;
937 case 8: t = 'Q'; goto alias;
938 default:
939 mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
940 }
941 break;
942 case 'i':
943 switch (sizeof(int)) {
944 case 2: t = 's'; goto alias;
945 case 4: t = 'l'; goto alias;
946 case 8: t = 'q'; goto alias;
947 default:
948 mrb_raisef(mrb, E_RUNTIME_ERROR, "mruby-pack does not support sizeof(int) == %S", mrb_fixnum_value(sizeof(int)));
949 }
950 break;
951 case 'L':
952 dir = PACK_DIR_LONG;
953 type = PACK_TYPE_INTEGER;
954 size = 4;
955 break;
956 case 'l':
957 dir = PACK_DIR_LONG;
958 type = PACK_TYPE_INTEGER;
959 size = 4;
960 flags |= PACK_FLAG_SIGNED;
961 break;
962 case 'm':
963 dir = PACK_DIR_BASE64;
964 type = PACK_TYPE_STRING;
965 flags |= PACK_FLAG_WIDTH;
966 break;
967 case 'N': /* = "L>" */
968 dir = PACK_DIR_LONG;
969 type = PACK_TYPE_INTEGER;
970 size = 4;
971 flags |= PACK_FLAG_GT;
972 break;
973 case 'n': /* = "S>" */
974 dir = PACK_DIR_SHORT;
975 type = PACK_TYPE_INTEGER;
976 size = 2;
977 flags |= PACK_FLAG_GT;
978 break;
979 case 'Q':
980 dir = PACK_DIR_QUAD;
981 type = PACK_TYPE_INTEGER;
982 size = 8;
983 break;
984 case 'q':
985 dir = PACK_DIR_QUAD;
986 type = PACK_TYPE_INTEGER;
987 size = 8;
988 flags |= PACK_FLAG_SIGNED;
989 break;
990 case 'S':
991 dir = PACK_DIR_SHORT;
992 type = PACK_TYPE_INTEGER;
993 size = 2;
994 break;
995 case 's':
996 dir = PACK_DIR_SHORT;
997 type = PACK_TYPE_INTEGER;
998 size = 2;
999 flags |= PACK_FLAG_SIGNED;
1000 break;
1001 case 'U':
1002 dir = PACK_DIR_UTF8;
1003 type = PACK_TYPE_INTEGER;
1004 break;
1005 case 'V': /* = "L<" */
1006 dir = PACK_DIR_LONG;
1007 type = PACK_TYPE_INTEGER;
1008 size = 4;
1009 flags |= PACK_FLAG_LT;
1010 break;
1011 case 'v': /* = "S<" */
1012 dir = PACK_DIR_SHORT;
1013 type = PACK_TYPE_INTEGER;
1014 size = 2;
1015 flags |= PACK_FLAG_LT;
1016 break;
1017 case 'x':
1018 dir = PACK_DIR_NUL;
1019 type = PACK_TYPE_NONE;
1020 break;
1021 case 'Z':
1022 dir = PACK_DIR_STR;
1023 type = PACK_TYPE_STRING;
1024 flags |= PACK_FLAG_WIDTH | PACK_FLAG_COUNT2 | PACK_FLAG_Z;
1025 break;
1026 default:
1027 dir = PACK_DIR_INVALID;
1028 type = PACK_TYPE_NONE;
1029 break;
1030 }
1031
1032 /* read suffix [0-9*_!<>] */
1033 while (tmpl->idx < tlen) {
1034 ch = tptr[tmpl->idx++];
1035 if (isdigit(ch)) {
1036 count = ch - '0';
1037 while (tmpl->idx < tlen && isdigit(tptr[tmpl->idx])) {
1038 count = count * 10 + (tptr[tmpl->idx++] - '0');
1039 }
1040 continue; /* special case */
1041 } else if (ch == '*') {
1042 count = -1;
1043 } else if (ch == '_' || ch == '!' || ch == '<' || ch == '>') {
1044 if (strchr("sSiIlLqQ", t) == NULL) {
1045 char ch_str = ch;
1046 mrb_raisef(mrb, E_ARGUMENT_ERROR, "'%S' allowed only after types sSiIlLqQ", mrb_str_new(mrb, &ch_str, 1));
1047 }
1048 if (ch == '_' || ch == '!') {
1049 flags |= PACK_FLAG_s;
1050 } else if (ch == '<') {
1051 flags |= PACK_FLAG_LT;
1052 } else if (ch == '>') {
1053 flags |= PACK_FLAG_GT;
1054 }
1055 } else {
1056 tmpl->idx--;
1057 break;
1058 }
1059 }
1060
1061 if ((flags & PACK_FLAG_LT) || (!(flags & PACK_FLAG_GT) && littleendian)) {
1062 flags |= PACK_FLAG_LITTLEENDIAN;
1063 }
1064
1065 *dirp = dir;
1066 *typep = type;
1067 *sizep = size;
1068 *countp = count;
1069 *flagsp = flags;
1070}
1071
1072static mrb_value
1073mrb_pack_pack(mrb_state *mrb, mrb_value ary)
1074{
1075 mrb_value o, result;
1076 mrb_int aidx;
1077 struct tmpl tmpl;
1078 long count;
1079 unsigned int flags;
1080 int dir, ridx, size, type;
1081
1082 prepare_tmpl(mrb, &tmpl);
1083
1084 result = mrb_str_new(mrb, NULL, 128); /* allocate initial buffer */
1085 aidx = 0;
1086 ridx = 0;
1087 while (has_tmpl(&tmpl)) {
1088 read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags);
1089
1090 if (dir == PACK_DIR_INVALID)
1091 continue;
1092 else if (dir == PACK_DIR_NUL) {
1093 ridx += pack_x(mrb, mrb_nil_value(), result, ridx, count, flags);
1094 continue;
1095 }
1096
1097 for (; aidx < RARRAY_LEN(ary); aidx++) {
1098 if (count == 0 && !(flags & PACK_FLAG_WIDTH))
1099 break;
1100
1101 o = mrb_ary_ref(mrb, ary, aidx);
1102 if (type == PACK_TYPE_INTEGER) {
1103 o = mrb_to_int(mrb, o);
1104 } else if (type == PACK_TYPE_FLOAT) {
1105 if (!mrb_float_p(o)) {
1106 o = mrb_funcall(mrb, o, "to_f", 0);
1107 }
1108 } else if (type == PACK_TYPE_STRING) {
1109 if (!mrb_string_p(o)) {
1110 mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %S into String", mrb_class_path(mrb, mrb_obj_class(mrb, o)));
1111 }
1112 }
1113
1114 switch (dir) {
1115 case PACK_DIR_CHAR:
1116 ridx += pack_c(mrb, o, result, ridx, flags);
1117 break;
1118 case PACK_DIR_SHORT:
1119 ridx += pack_s(mrb, o, result, ridx, flags);
1120 break;
1121 case PACK_DIR_LONG:
1122 ridx += pack_l(mrb, o, result, ridx, flags);
1123 break;
1124 case PACK_DIR_QUAD:
1125 ridx += pack_q(mrb, o, result, ridx, flags);
1126 break;
1127 case PACK_DIR_BASE64:
1128 ridx += pack_m(mrb, o, result, ridx, count, flags);
1129 break;
1130 case PACK_DIR_HEX:
1131 ridx += pack_h(mrb, o, result, ridx, count, flags);
1132 break;
1133 case PACK_DIR_STR:
1134 ridx += pack_a(mrb, o, result, ridx, count, flags);
1135 break;
1136 case PACK_DIR_DOUBLE:
1137 ridx += pack_double(mrb, o, result, ridx, flags);
1138 break;
1139 case PACK_DIR_FLOAT:
1140 ridx += pack_float(mrb, o, result, ridx, flags);
1141 break;
1142 case PACK_DIR_UTF8:
1143 ridx += pack_utf8(mrb, o, result, ridx, count, flags);
1144 break;
1145 default:
1146 break;
1147 }
1148 if (dir == PACK_DIR_STR) { /* always consumes 1 entry */
1149 aidx++;
1150 break;
1151 }
1152 if (count > 0) {
1153 count--;
1154 }
1155 }
1156 }
1157
1158 mrb_str_resize(mrb, result, ridx);
1159 return result;
1160}
1161
1162static mrb_value
1163mrb_pack_unpack(mrb_state *mrb, mrb_value str)
1164{
1165 mrb_value result;
1166 struct tmpl tmpl;
1167 long count;
1168 unsigned int flags;
1169 int dir, size, srcidx, srclen, type;
1170 const unsigned char *sptr;
1171
1172 prepare_tmpl(mrb, &tmpl);
1173
1174 srcidx = 0;
1175 srclen = RSTRING_LEN(str);
1176
1177 result = mrb_ary_new(mrb);
1178 while (has_tmpl(&tmpl)) {
1179 read_tmpl(mrb, &tmpl, &dir, &type, &size, &count, &flags);
1180
1181 if (dir == PACK_DIR_INVALID)
1182 continue;
1183 else if (dir == PACK_DIR_NUL) {
1184 srcidx += unpack_x(mrb, sptr, srclen - srcidx, result, count, flags);
1185 continue;
1186 }
1187
1188 if (flags & PACK_FLAG_COUNT2) {
1189 sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx;
1190 switch (dir) {
1191 case PACK_DIR_HEX:
1192 srcidx += unpack_h(mrb, sptr, srclen - srcidx, result, count, flags);
1193 break;
1194 case PACK_DIR_STR:
1195 srcidx += unpack_a(mrb, sptr, srclen - srcidx, result, count, flags);
1196 break;
1197 }
1198 continue;
1199 }
1200
1201 while (count != 0) {
1202 if (srclen - srcidx < size) {
1203 while (count-- > 0) {
1204 mrb_ary_push(mrb, result, mrb_nil_value());
1205 }
1206 break;
1207 }
1208
1209 sptr = (const unsigned char *)RSTRING_PTR(str) + srcidx;
1210 switch (dir) {
1211 case PACK_DIR_CHAR:
1212 srcidx += unpack_c(mrb, sptr, srclen - srcidx, result, flags);
1213 break;
1214 case PACK_DIR_SHORT:
1215 srcidx += unpack_s(mrb, sptr, srclen - srcidx, result, flags);
1216 break;
1217 case PACK_DIR_LONG:
1218 srcidx += unpack_l(mrb, sptr, srclen - srcidx, result, flags);
1219 break;
1220 case PACK_DIR_QUAD:
1221 srcidx += unpack_q(mrb, sptr, srclen - srcidx, result, flags);
1222 break;
1223 case PACK_DIR_BASE64:
1224 srcidx += unpack_m(mrb, sptr, srclen - srcidx, result, flags);
1225 break;
1226 case PACK_DIR_FLOAT:
1227 srcidx += unpack_float(mrb, sptr, srclen - srcidx, result, flags);
1228 break;
1229 case PACK_DIR_DOUBLE:
1230 srcidx += unpack_double(mrb, sptr, srclen - srcidx, result, flags);
1231 break;
1232 case PACK_DIR_UTF8:
1233 srcidx += unpack_utf8(mrb, sptr, srclen - srcidx, result, flags);
1234 break;
1235 default:
1236 mrb_raise(mrb, E_RUNTIME_ERROR, "mruby-pack's bug");
1237 }
1238 if (count > 0) {
1239 count--;
1240 }
1241 }
1242 }
1243
1244 return result;
1245}
1246
1247void
1248mrb_mruby_pack_gem_init(mrb_state *mrb)
1249{
1250 littleendian = check_little_endian();
1251 make_base64_dec_tab();
1252
1253 mrb_define_method(mrb, mrb->array_class, "pack", mrb_pack_pack, MRB_ARGS_REQ(1));
1254 mrb_define_method(mrb, mrb->string_class, "unpack", mrb_pack_unpack, MRB_ARGS_REQ(1));
1255}
1256
1257void
1258mrb_mruby_pack_gem_final(mrb_state *mrb)
1259{
1260}
Note: See TracBrowser for help on using the repository browser.