Module: Protocol::ZMTP::Z85

Defined in:
lib/protocol/zmtp/z85.rb

Overview

Z85 encoding/decoding (ZeroMQ RFC 32).

Encodes binary data in printable ASCII using an 85-character alphabet. Input length must be a multiple of 4 bytes; output is 5/4 the size.

Constant Summary collapse

CHARS =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#".freeze
DECODE =
Array.new(128, -1)
BASE =
85

Class Method Summary collapse

Class Method Details

.decode(string) ⇒ String

Decodes a Z85-encoded string back to binary data.

Parameters:

  • string (String)

    Z85-encoded string (length must be a multiple of 5)

Returns:

  • (String)

    decoded binary data (4/5 the size of the input)

Raises:

  • (ArgumentError)

    if string length is not a multiple of 5 or contains invalid characters



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/protocol/zmtp/z85.rb', line 47

def self.decode(string)
  raise ArgumentError, "string length must be a multiple of 5 (got #{string.bytesize})" unless (string.bytesize % 5).zero?

  out = String.new(capacity: string.bytesize * 4 / 5, encoding: Encoding::BINARY)
  i = 0
  while i < string.bytesize
    value = 0
    5.times do |j|
      byte = string.getbyte(i + j)
      d = byte < 128 ? DECODE[byte] : -1
      raise ArgumentError, "invalid Z85 character: #{string[i + j].inspect}" if d == -1
      value = value * BASE + d
    end
    out << ((value >> 24) & 0xFF).chr
    out << ((value >> 16) & 0xFF).chr
    out << ((value >> 8) & 0xFF).chr
    out << (value & 0xFF).chr
    i += 5
  end
  out
end

.encode(data) ⇒ String

Encodes binary data to a Z85-encoded ASCII string.

Parameters:

  • data (String)

    binary data (length must be a multiple of 4)

Returns:

  • (String)

    Z85-encoded string (5/4 the size of the input)

Raises:

  • (ArgumentError)

    if data length is not a multiple of 4



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/protocol/zmtp/z85.rb', line 24

def self.encode(data)
  data = data.b
  raise ArgumentError, "data length must be a multiple of 4 (got #{data.bytesize})" unless (data.bytesize % 4).zero?

  out = String.new(capacity: data.bytesize * 5 / 4)
  i = 0
  while i < data.bytesize
    value = data.getbyte(i) << 24 | data.getbyte(i + 1) << 16 |
            data.getbyte(i + 2) << 8 | data.getbyte(i + 3)
    4.downto(0) do |j|
      out << CHARS[(value / (BASE**j)) % BASE]
    end
    i += 4
  end
  out
end