File: bigint.rb

package info (click to toggle)
ruby-msgpack 1.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 972 kB
  • sloc: ruby: 4,789; ansic: 4,309; java: 1,809; makefile: 4
file content (69 lines) | stat: -rw-r--r-- 1,705 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# frozen_string_literal: true

module MessagePack
  module Bigint
    # We split the bigint in 32bits chunks so that individual part fits into
    # a MRI immediate Integer.
    CHUNK_BITLENGTH = 32
    FORMAT = 'CL>*'

    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#[]
      # This allows to not allocate any Integer.
      def self.to_msgpack_ext(bigint)
        members = []

        if bigint < 0
          bigint = -bigint
          members << 1
        else
          members << 0
        end

        offset = 0
        length = bigint.bit_length
        while offset < length
          members << bigint[offset, CHUNK_BITLENGTH]
          offset += CHUNK_BITLENGTH
        end

        members.pack(FORMAT)
      end
    else
      # On 2.6 and older since we can't address arbitrary bitranges, so we fallback to shifting the bigint.
      # This means that after each shift, we may allocate another Integer instance.
      BASE = (2**CHUNK_BITLENGTH) - 1
      def self.to_msgpack_ext(bigint)
        members = []

        if bigint < 0
          bigint = -bigint
          members << 1
        else
          members << 0
        end

        while bigint > 0
          members << (bigint & BASE)
          bigint = bigint >> CHUNK_BITLENGTH
        end

        members.pack(FORMAT)
      end
    end

    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

      sign == 0 ? sum : -sum
    end
  end
end