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

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

mrubyを2.1.1に更新

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