File: encode.rb

package info (click to toggle)
ruby-bert 1.1.6-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 232 kB
  • sloc: ruby: 802; ansic: 345; makefile: 7
file content (139 lines) | stat: -rw-r--r-- 2,769 bytes parent folder | download
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
module BERT
  class Encode
    include Types

    attr_accessor :out

    def initialize(out)
      self.out = out
    end

    def self.encode(data)
      io = StringIO.new
      io.set_encoding('binary') if io.respond_to?(:set_encoding)
      self.new(io).write_any(data)
      io.string
    end

    def write_any obj
      write_1 MAGIC
      write_any_raw obj
    end

    def write_any_raw obj
      case obj
        when Symbol then write_symbol(obj)
        when Integer then write_fixnum(obj)
        when Float then write_float(obj)
        when Tuple then write_tuple(obj)
        when Array then write_list(obj)
        when String then write_binary(obj)
        else
          fail(obj)
      end
    end

    def write_1(byte)
      out.write([byte].pack("C"))
    end

    def write_2(short)
      out.write([short].pack("n"))
    end

    def write_4(long)
      out.write([long].pack("N"))
    end

    def write_string(string)
      out.write(string)
    end

    def write_boolean(bool)
      write_symbol(bool.to_s.to_sym)
    end

    def write_symbol(sym)
      fail(sym) unless sym.is_a?(Symbol)
      data = sym.to_s
      write_1 ATOM
      write_2 data.bytesize
      write_string data
    end

    def write_fixnum(num)
      if num >= 0 && num < 256
        write_1 SMALL_INT
        write_1 num
      elsif num <= MAX_INT && num >= MIN_INT
        write_1 INT
        write_4 num
      else
        write_bignum num
      end
    end

    def write_float(float)
      write_1 FLOAT
      write_string format("%15.15e", float).ljust(31, "\000")
    end

    def write_bignum(num)
      n = (num.abs.to_s(2).size / 8.0).ceil
      if n < 256
        write_1 SMALL_BIGNUM
        write_1 n
        write_bignum_guts(num)
      else
        write_1 LARGE_BIGNUM
        write_4 n
        write_bignum_guts(num)
      end
    end

    def write_bignum_guts(num)
      write_1 (num >= 0 ? 0 : 1)
      num = num.abs
      while num != 0
        rem = num % 256
        write_1 rem
        num = num >> 8
      end
    end

    def write_tuple(data)
      fail(data) unless data.is_a? Array

      if data.length < 256
        write_1 SMALL_TUPLE
        write_1 data.length
      else
        write_1 LARGE_TUPLE
        write_4 data.length
      end

      data.each { |e| write_any_raw e }
    end

    def write_list(data)
      fail(data) unless data.is_a? Array
      write_1 NIL and return if data.empty?
      write_1 LIST
      write_4 data.length
      data.each{|e| write_any_raw e }
      write_1 NIL
    end

    def write_binary(data)
      write_1 BIN
      write_4 data.bytesize
      write_string data
    end

    private

    def fail(obj)
      raise "Cannot encode to erlang external format: #{obj.inspect}"
    end
  end
end