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
|