Class: CZTop::Metadata

Inherits:
Object
  • Object
show all
Defined in:
lib/cztop/metadata.rb

Overview

Useful to encode and decode metadata as defined by ZMTP.

ABNF:

metadata = *property
property = name value
name = OCTET 1*255name-char
name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
value = 4OCTET *OCTET       ; Size in network byte order

Defined Under Namespace

Classes: InvalidData

Constant Summary collapse

VALUE_MAXLEN =
2**31-1
NAME_REGEX =

regular expression used to validate property names

/\A[[:alnum:]_.+-]{1,255}\Z/.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(properties) ⇒ Metadata

Returns a new instance of Metadata.

Parameters:

  • properties (Hash<Symbol, String>)

    the properties as loaded by load


83
84
85
# File 'lib/cztop/metadata.rb', line 83

def initialize(properties)
  @properties = properties
end

Class Method Details

.dump(metadata) ⇒ String

Parameters:

  • metadata (Hash<Symbol, #to_s>)

Returns:

  • (String)

Raises:

  • (ArgumentError)

    when properties have an invalid, too long, or duplicated name, or when a value is too long


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/cztop/metadata.rb', line 30

def self.dump()
  ic_names = Set.new
  .map do |k, v|
    ic_name = k.to_sym.downcase
    if ic_names.include?(ic_name)
      raise ArgumentError, "property #{k.inspect}: duplicate name"
    else
      ic_names << ic_name
    end
    name = k.to_s
    if NAME_REGEX !~ name
      raise ArgumentError, "property #{k.inspect}: invalid name"
    end
    value = v.to_s
    if value.bytesize > VALUE_MAXLEN
      raise ArgumentError, "property #{k.inspect}: value too long"
    end
    [name.size, name, value.bytesize, value].pack("CA*NA*")
  end.join
end

.load(data) ⇒ Hash

Parameters:

  • data (String, Frame, #to_s)

    the data representing the metadata

Returns:

  • (Hash)

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
# File 'lib/cztop/metadata.rb', line 53

def self.load(data)
  properties = {}
  consumed = 0
  while consumed < data.bytesize # while there are bytes to read
    # read property name
    name_length = data.byteslice(consumed).unpack("C").first # never nil
    raise InvalidData, "zero-length property name" if name_length.zero?
    name = data.byteslice(consumed + 1, name_length)
    raise InvalidData, "incomplete name" if name.bytesize != name_length
    name_sym = name.to_sym.downcase
    if properties.has_key?(name_sym)
      raise InvalidData, "property #{name.inspect}: duplicate name"
    end
    consumed += 1 + name.bytesize

    # read property value
    value_length = data.byteslice(consumed, 4).unpack("N").first or
      raise InvalidData, "incomplete length"
    value = data.byteslice(consumed + 4, value_length)
    raise InvalidData, "incomplete value" if value.bytesize != value_length
    consumed += 4 + value.bytesize

    # remember
    properties[name_sym] = value
  end
  new(properties)
end

Instance Method Details

#[](name) ⇒ String

Gets the value corresponding to a property name. The case of the name is insignificant.

Parameters:

  • name (Symbol, String)

    the property name

Returns:

  • (String)

    the value


91
92
93
# File 'lib/cztop/metadata.rb', line 91

def [](name)
  @properties[name.to_sym.downcase]
end

#to_hHash<Symbol, String] all properties

Returns Hash<Symbol, String] all properties.

Returns:

  • (Hash<Symbol, String] all properties)

    Hash<Symbol, String] all properties


96
97
98
# File 'lib/cztop/metadata.rb', line 96

def to_h
  @properties
end