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



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

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



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/cztop/metadata.rb', line 35

def self.dump()
  ic_names = Set.new

  .map do |k, v|
    ic_name = k.to_sym.downcase
    raise ArgumentError, "property #{k.inspect}: duplicate name" if ic_names.include? ic_name

    ic_names << ic_name

    name = k.to_s
    raise ArgumentError, "property #{k.inspect}: invalid name" if NAME_REGEX !~ name

    value = v.to_s
    raise ArgumentError, "property #{k.inspect}: value too long" if value.bytesize > VALUE_MAXLEN

    [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)


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

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).unpack1('C') # 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
    raise InvalidData, "property #{name.inspect}: duplicate name" if properties.key?(name_sym)

    consumed += 1 + name.bytesize

    # read property value
    value_length = data.byteslice(consumed, 4).unpack1('N') 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



100
101
102
# File 'lib/cztop/metadata.rb', line 100

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



106
107
108
# File 'lib/cztop/metadata.rb', line 106

def to_h
  @properties
end