From 6bf285b17f1978de23bab8a2398dc5a9cfc9e730 Mon Sep 17 00:00:00 2001 From: Shizuo Fujita Date: Sun, 11 Oct 2026 22:31:44 +0900 Subject: [PATCH] Speed up decoding of oversized integers Bigint.from_msgpack_ext rebuilt the integer by shifting and adding one 32-bit chunk at a time. Each step copies the whole growing bignum, so decoding was quadratic in the payload length (a 1 MiB ext payload took over 20 seconds). Convert the chunks to a hex string and parse it in a single String#to_i(16), which runs in linear time. Co-Authored-By: Claude Opus 4.8 --- lib/msgpack/bigint.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/msgpack/bigint.rb b/lib/msgpack/bigint.rb index 95b85a54..f883d14d 100644 --- a/lib/msgpack/bigint.rb +++ b/lib/msgpack/bigint.rb @@ -5,7 +5,8 @@ module Bigint # We split the bigint in 32bits chunks so that individual part fits into # a MRI immediate Integer. CHUNK_BITLENGTH = 32 - FORMAT = 'CL>*' + CHUNK_FORMAT = 'L>*' + FORMAT = "C#{CHUNK_FORMAT}" if Integer.instance_method(:[]).arity != 1 # Ruby 2.7 and newer # Starting from Ruby 2.7 we can address arbitrary bitranges inside an Integer with Integer#[] @@ -56,12 +57,7 @@ def self.from_msgpack_ext(data) parts = data.unpack(FORMAT) sign = parts.shift - sum = parts.pop.to_i - - parts.reverse_each do |part| - sum = sum << CHUNK_BITLENGTH - sum += part - end + sum = parts.reverse.pack(CHUNK_FORMAT).unpack1('H*').to_i(16) sign == 0 ? sum : -sum end