Refine Array#pack r/R directives

* remove the temporary buffer object.
* simplify the condition under which an extra byte is required for
  sign extension.
* in the case of `R`, raise an error earlier before packing for the
  negative number.
This commit is contained in:
Nobuyoshi Nakada 2025-12-19 13:14:46 +09:00
parent 275e53e452
commit 67d4396dc9
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
Notes: git 2026-05-06 07:02:32 +00:00
2 changed files with 25 additions and 23 deletions

3
depend
View File

@ -10409,11 +10409,14 @@ pack.$(OBJEXT): $(CCAN_DIR)/str/str.h
pack.$(OBJEXT): $(hdrdir)/ruby/ruby.h
pack.$(OBJEXT): $(top_srcdir)/internal/array.h
pack.$(OBJEXT): $(top_srcdir)/internal/basic_operators.h
pack.$(OBJEXT): $(top_srcdir)/internal/bignum.h
pack.$(OBJEXT): $(top_srcdir)/internal/bits.h
pack.$(OBJEXT): $(top_srcdir)/internal/box.h
pack.$(OBJEXT): $(top_srcdir)/internal/compilers.h
pack.$(OBJEXT): $(top_srcdir)/internal/fixnum.h
pack.$(OBJEXT): $(top_srcdir)/internal/gc.h
pack.$(OBJEXT): $(top_srcdir)/internal/imemo.h
pack.$(OBJEXT): $(top_srcdir)/internal/numeric.h
pack.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
pack.$(OBJEXT): $(top_srcdir)/internal/serial.h
pack.$(OBJEXT): $(top_srcdir)/internal/set_table.h

45
pack.c
View File

@ -19,6 +19,7 @@
#include "internal.h"
#include "internal/array.h"
#include "internal/bits.h"
#include "internal/numeric.h"
#include "internal/string.h"
#include "internal/symbol.h"
#include "internal/variable.h"
@ -677,43 +678,41 @@ pack_pack(rb_execution_context_t *ec, VALUE ary, VALUE fmt, VALUE buffer)
}
while (len-- > 0) {
size_t numbytes;
int sign;
size_t numbytes, nlz_bits;
int sign, extra = 0;
char *cp;
const long start = RSTRING_LEN(res);
from = NEXTFROM;
from = rb_to_int(from);
numbytes = rb_absint_numwords(from, 7, NULL);
if (numbytes == 0)
numbytes = 1;
VALUE buf = rb_str_new(NULL, numbytes);
sign = rb_integer_pack(from, RSTRING_PTR(buf), RSTRING_LEN(buf), 1, 1, pack_flags);
if (sign < 0 && type == 'R') {
if (type == 'R' && rb_int_negative_p(from)) {
rb_raise(rb_eArgError, "can't encode negative numbers in ULEB128");
}
if (type == 'r') {
/* Check if we need an extra byte for sign extension */
unsigned char last_byte = (unsigned char)RSTRING_PTR(buf)[numbytes - 1];
if ((sign >= 0 && (last_byte & 0x40)) || /* positive but sign bit set */
(sign < 0 && !(last_byte & 0x40))) { /* negative but sign bit clear */
/* Need an extra byte */
rb_str_resize(buf, numbytes + 1);
RSTRING_PTR(buf)[numbytes] = sign < 0 ? 0x7f : 0x00;
numbytes++;
}
numbytes = rb_absint_numwords(from, 7, &nlz_bits);
if (numbytes == 0) {
numbytes = 1;
}
else if (nlz_bits == 0 && type == 'r') {
/* No leading zero bits, we need an extra byte for sign extension */
extra = 1;
}
rb_str_modify_expand(res, numbytes + extra);
cp = RSTRING_PTR(res) + start;
sign = rb_integer_pack(from, cp, numbytes, 1, 1, pack_flags);
if (extra) {
/* Need an extra byte */
cp[numbytes++] = sign < 0 ? 0x7f : 0x00;
}
rb_str_set_len(res, start + numbytes);
cp = RSTRING_PTR(buf);
while (1 < numbytes) {
*cp |= 0x80;
cp++;
numbytes--;
}
rb_str_buf_cat(res, RSTRING_PTR(buf), RSTRING_LEN(buf));
}
}
break;