|
19 | 19 | #include "internal.h" |
20 | 20 | #include "internal/array.h" |
21 | 21 | #include "internal/bits.h" |
| 22 | +#include "internal/numeric.h" |
22 | 23 | #include "internal/string.h" |
23 | 24 | #include "internal/symbol.h" |
24 | 25 | #include "internal/variable.h" |
@@ -677,43 +678,41 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer) |
677 | 678 | } |
678 | 679 |
|
679 | 680 | while (len-- > 0) { |
680 | | - size_t numbytes; |
681 | | - int sign; |
| 681 | + size_t numbytes, nlz_bits; |
| 682 | + int sign, extra = 0; |
682 | 683 | char *cp; |
| 684 | + long start = RSTRING_LEN(res); |
683 | 685 |
|
684 | 686 | from = NEXTFROM; |
685 | 687 | from = rb_to_int(from); |
686 | | - numbytes = rb_absint_numwords(from, 7, NULL); |
687 | | - if (numbytes == 0) |
688 | | - numbytes = 1; |
689 | | - VALUE buf = rb_str_new(NULL, numbytes); |
690 | | - |
691 | | - sign = rb_integer_pack(from, RSTRING_PTR(buf), RSTRING_LEN(buf), 1, 1, pack_flags); |
692 | | - |
693 | | - if (sign < 0 && type == 'R') { |
| 688 | + if (type == 'R' && rb_int_negative_p(from)) { |
694 | 689 | rb_raise(rb_eArgError, "can't encode negative numbers in ULEB128"); |
695 | 690 | } |
696 | 691 |
|
697 | | - if (type == 'r') { |
698 | | - /* Check if we need an extra byte for sign extension */ |
699 | | - unsigned char last_byte = (unsigned char)RSTRING_PTR(buf)[numbytes - 1]; |
700 | | - if ((sign >= 0 && (last_byte & 0x40)) || /* positive but sign bit set */ |
701 | | - (sign < 0 && !(last_byte & 0x40))) { /* negative but sign bit clear */ |
702 | | - /* Need an extra byte */ |
703 | | - rb_str_resize(buf, numbytes + 1); |
704 | | - RSTRING_PTR(buf)[numbytes] = sign < 0 ? 0x7f : 0x00; |
705 | | - numbytes++; |
706 | | - } |
| 692 | + numbytes = rb_absint_numwords(from, 7, &nlz_bits); |
| 693 | + if (numbytes == 0) { |
| 694 | + numbytes = 1; |
707 | 695 | } |
| 696 | + else if (nlz_bits == 0 && type == 'r') { |
| 697 | + /* No leading zero bits, we need an extra byte for sign extension */ |
| 698 | + extra = 1; |
| 699 | + } |
| 700 | + rb_str_modify_expand(res, numbytes + extra); |
| 701 | + |
| 702 | + cp = RSTRING_PTR(res) + start; |
| 703 | + sign = rb_integer_pack(from, cp, numbytes, 1, 1, pack_flags); |
| 704 | + |
| 705 | + if (extra) { |
| 706 | + /* Need an extra byte */ |
| 707 | + cp[numbytes++] = sign < 0 ? 0x7f : 0x00; |
| 708 | + } |
| 709 | + rb_str_set_len(res, start + numbytes); |
708 | 710 |
|
709 | | - cp = RSTRING_PTR(buf); |
710 | 711 | while (1 < numbytes) { |
711 | 712 | *cp |= 0x80; |
712 | 713 | cp++; |
713 | 714 | numbytes--; |
714 | 715 | } |
715 | | - |
716 | | - rb_str_buf_cat(res, RSTRING_PTR(buf), RSTRING_LEN(buf)); |
717 | 716 | } |
718 | 717 | } |
719 | 718 | break; |
|
0 commit comments