class Qpid::Proton::Codec::Data

The Data class provides an interface for decoding, extracting, creating, and encoding arbitrary AMQP data. A Data object contains a tree of AMQP values. Leaf nodes in this tree correspond to scalars in the AMQP type system such as INT or STRING. Interior nodes in this tree correspond to compound values in the AMQP type system such as LIST,MAP, ARRAY, or DESCRIBED. The root node of the tree is the Data object itself and can have an arbitrary number of children.

A Data object maintains the notion of the current sibling node and a current parent node. Siblings are ordered within their parent. Values are accessed and/or added by using the next, prev, enter, and exit methods to navigate to the desired location in the tree and using the supplied variety of mutator and accessor methods to access or add a value of the desired type.

The mutator methods will always add a value after the current node in the tree. If the current node has a next sibling the mutator method will overwrite the value on this node. If there is no current node or the current node has no next sibling then one will be added. The accessor methods always set the added/modified node to the current node. The accessor methods read the value of the current node and do not change which node is current.

The following types of scalar values are supported:

The following types of compound values are supported:

Public Class Methods

finalize!(data, free) click to toggle source

@private

# File lib/codec/data.rb, line 102
def self.finalize!(data, free)
  proc {
    Cproton.pn_data_free(data) if free
  }
end
new(capacity = 16) click to toggle source

Creates a new instance with the specified capacity.

@param capacity [Integer, Object] The initial capacity or content.

# File lib/codec/data.rb, line 87
def initialize(capacity = 16)
  # TODO aconway 2017-08-11: error prone, confusion between capacity and Integer content.
  if capacity.is_a?(Integer)
    @data = Cproton.pn_data(capacity.to_i)
    @free = true
  else
    @data = capacity
    @free = false
  end

  # destructor
  ObjectSpace.define_finalizer(self, self.class.finalize!(@data, @free))
end

Public Instance Methods

array() click to toggle source

If the current node is an array, returns a tuple of the element count, a boolean indicating whether the array is described, and the type of each element. Otherwise it returns +(0, false, nil).

Array data can be accessed by entering the array.

@example

# get the details of thecurrent array
count, described, array_type = @data.array

# enter the node
data.enter

# get the next node
data.next
puts "Descriptor: #{data.symbol}" if described
(0...count).each do
  @data.next
  puts "Element: #{@data.string}"
end
# File lib/codec/data.rb, line 365
def array
  count = Cproton.pn_data_get_array(@data)
  described = Cproton.pn_data_is_array_described(@data)
  array_type = Cproton.pn_data_get_array_type(@data)
  return nil if array_type == -1
  [count, described, Mapping.for_code(array_type) ]
end
binary() click to toggle source

If the current node is binary, returns its value. Otherwise, it returns an empty string (“”).

@return [String] The binary string.

@see string

# File lib/codec/data.rb, line 823
def binary
  Qpid::Proton::Types::BinaryString.new(Cproton.pn_data_get_binary(@data))
end
binary=(value) click to toggle source

Puts a binary value.

A binary string is encoded as an ASCII 8-bit string value. This is in contranst to other strings, which are treated as UTF-8 encoded.

@param value [String] An arbitrary string value.

@see string=

# File lib/codec/data.rb, line 812
def binary=(value)
  check(Cproton.pn_data_put_binary(@data, value))
end
bool() click to toggle source

If the current node is a boolean, then it returns the value. Otherwise, it returns false.

@return [Boolean] The boolean value.

# File lib/codec/data.rb, line 482
def bool
  Cproton.pn_data_get_bool(@data)
end
bool=(value) click to toggle source

Puts a boolean value.

@param value [Boolean] The boolean value.

# File lib/codec/data.rb, line 473
def bool=(value)
  check(Cproton.pn_data_put_bool(@data, value))
end
byte() click to toggle source

If the current node is an byte, returns its value. Otherwise, it returns 0.

@return [Integer] The byte value.

# File lib/codec/data.rb, line 516
def byte
  Cproton.pn_data_get_byte(@data)
end
byte=(value) click to toggle source

Puts a byte value.

@param value [Integer] The byte value.

# File lib/codec/data.rb, line 507
def byte=(value)
  check(Cproton.pn_data_put_byte(@data, value))
end
char() click to toggle source

If the current node is a character, returns its value. Otherwise, returns 0.

@return [Integer] The character value.

# File lib/codec/data.rb, line 604
def char
  Cproton.pn_data_get_char(@data)
end
char=(value) click to toggle source

Puts a character value.

@param value [Integer] The character value.

# File lib/codec/data.rb, line 595
def char=(value)
  check(Cproton.pn_data_put_char(@data, value))
end
clear() click to toggle source

Clears the object.

# File lib/codec/data.rb, line 119
def clear
  Cproton.pn_data_clear(@data)
end
decimal128() click to toggle source

If the current node is a decimal128, returns its value. Otherwise, returns 0.

@return [Integer] The decimal128 value.

# File lib/codec/data.rb, line 745
def decimal128
  value = ""
  Cproton.pn_data_get_decimal128(@data).each{|val| value += ("%02x" % val)}
  value.to_i(16)
end
decimal128=(value) click to toggle source

Puts a decimal128 value.

@param value [Integer] The decimal128 value.

# File lib/codec/data.rb, line 732
def decimal128=(value)
  raise TypeError, "invalid decimal128 value: #{value}" if value.nil?
  value = value.to_s(16).rjust(32, "0")
  bytes = []
  value.scan(/(..)/) {|v| bytes << v[0].to_i(16)}
  check(Cproton.pn_data_put_decimal128(@data, bytes))
end
decimal32() click to toggle source

If the current node is a decimal32, returns its value. Otherwise, returns 0.

@return [Integer] The decimal32 value.

# File lib/codec/data.rb, line 707
def decimal32
  Cproton.pn_data_get_decimal32(@data)
end
decimal32=(value) click to toggle source

Puts a decimal32 value.

@param value [Integer] The decimal32 value.

# File lib/codec/data.rb, line 698
def decimal32=(value)
  check(Cproton.pn_data_put_decimal32(@data, value))
end
decimal64() click to toggle source

If the current node is a decimal64, returns its value. Otherwise, it returns 0.

@return [Integer] The decimal64 value.

# File lib/codec/data.rb, line 724
def decimal64
  Cproton.pn_data_get_decimal64(@data)
end
decimal64=(value) click to toggle source

Puts a decimal64 value.

@param value [Integer] The decimal64 value.

# File lib/codec/data.rb, line 715
def decimal64=(value)
  check(Cproton.pn_data_put_decimal64(@data, value))
end
decode(encoded) click to toggle source

Decodes the first value from supplied AMQP data and returns the number of bytes consumed.

@param encoded [String] The encoded data.

@example

# SCENARIO: A string of encoded data, @encoded, contains the text
#           of "This is a test." and is passed to an instance of Data
#           for decoding.

@data.decode(@encoded)
@data.string #=> "This is a test."
# File lib/codec/data.rb, line 222
def decode(encoded)
  check(Cproton.pn_data_decode(@data, encoded, encoded.length))
end
described?() click to toggle source

Checks if the current node is a described value.

The described and value may be accessed by entering the described value.

@example

if @data.described?
  @data.enter
  puts "The symbol is #{@data.symbol}"
  puts "The value is #{@data.string}"
end
# File lib/codec/data.rb, line 422
def described?
  Cproton.pn_data_is_described(@data)
end
double() click to toggle source

If the current node is a double, returns its value. Otherwise, returns 0.

@return [Float] The double precision floating point value.

# File lib/codec/data.rb, line 690
def double
  Cproton.pn_data_get_double(@data)
end
double=(value) click to toggle source

Puts a double value.

@param value [Float] The double precision floating point value.

# File lib/codec/data.rb, line 681
def double=(value)
  check(Cproton.pn_data_put_double(@data, value))
end
encode() click to toggle source

Returns a representation of the data encoded in AMQP format.

@return [String] The context of the Data as an AMQP data string.

@example

@data.string = "This is a test."
@encoded = @data.encode

# @encoded now contains the text "This is a test." encoded for
# AMQP transport.
# File lib/codec/data.rb, line 194
def encode
  buffer = "\0"*1024
  loop do
    cd = Cproton.pn_data_encode(@data, buffer, buffer.length)
    if cd == Cproton::PN_OVERFLOW
      buffer *= 2
    elsif cd >= 0
      return buffer[0...cd]
    else
      check(cd)
    end
  end
end
enter() click to toggle source

Sets the parent node to the current node and clears the current node.

Clearing the current node sets it before the first child.

# File lib/codec/data.rb, line 154
def enter
  Cproton.pn_data_enter(@data)
end
exit() click to toggle source

Sets the current node to the parent node and the parent node to its own parent.

# File lib/codec/data.rb, line 161
def exit
  Cproton.pn_data_exit(@data)
end
float() click to toggle source

If the current node is a float, returns its value. Otherwise, returns 0.

@return [Float] The floating point value.

# File lib/codec/data.rb, line 673
def float
  Cproton.pn_data_get_float(@data)
end
float=(value) click to toggle source

Puts a float value.

@param value [Float] The floating point value.

# File lib/codec/data.rb, line 664
def float=(value)
  check(Cproton.pn_data_put_float(@data, value))
end
get() click to toggle source

Get the current value as a single object.

@return [Object] The current node's object.

@see type_code @see type

# File lib/codec/data.rb, line 874
def get
  type.get(self);
end
get_array() click to toggle source

@private

# File lib/codec/data.rb, line 374
def get_array
  ::Array.proton_get(self)
end
get_described() click to toggle source

@private

# File lib/codec/data.rb, line 398
def get_described
  raise TypeError, "not a described type" unless self.described?
  self.enter
  self.next
  type = self.type
  descriptor = type.get(self)
  self.next
  type = self.type
  value = type.get(self)
  self.exit
  Qpid::Proton::Types::Described.new(descriptor, value)
end
get_map() click to toggle source

@private

# File lib/codec/data.rb, line 304
def get_map
  ::Hash.proton_data_get(self)
end
int() click to toggle source

If the current node is an integer, returns its value. Otherwise, returns 0.

@return [Integer] The integer value.

# File lib/codec/data.rb, line 587
def int
  Cproton.pn_data_get_int(@data)
end
int=(value) click to toggle source

Puts an integer value.

Options

  • value - the integer value

# File lib/codec/data.rb, line 578
def int=(value)
  check(Cproton.pn_data_put_int(@data, value))
end
list() click to toggle source

If the current node is a list, this returns the number of elements. Otherwise, it returns zero.

List elements can be accessed by entering the list.

@example

count = @data.list
@data.enter
(0...count).each
  type = @data.next
  puts "Value: #{@data.string}" if type == STRING
  # ... process other node types
end
# File lib/codec/data.rb, line 259
def list
  Cproton.pn_data_get_list(@data)
end
long() click to toggle source

If the current node is a long, returns its value. Otherwise, returns 0.

@return [Integer] The long value.

# File lib/codec/data.rb, line 638
def long
  Cproton.pn_data_get_long(@data)
end
long=(value) click to toggle source

Puts a long value.

@param value [Integer] The long value.

# File lib/codec/data.rb, line 631
def long=(value)
  check(Cproton.pn_data_put_long(@data, value))
end
map() click to toggle source

If the current node is a map, this returns the number of child elements. Otherwise, it returns zero.

Key/value pairs can be accessed by entering the map.

@example

count = @data.map
@data.enter
(0...count).each do
  type = @data.next
  puts "Key=#{@data.string}" if type == STRING
  # ... process other key types
  type = @data.next
  puts "Value=#{@data.string}" if type == STRING
  # ... process other value types
end
@data.exit
# File lib/codec/data.rb, line 299
def map
  Cproton.pn_data_get_map(@data)
end
next() click to toggle source

Advances the current node to its next sibling and returns its types.

If there is no next sibling the current node remains unchanged and nil is returned.

# File lib/codec/data.rb, line 137
def next
  Cproton.pn_data_next(@data)
end
null() click to toggle source

Puts a null value.

# File lib/codec/data.rb, line 428
def null
  check(Cproton.pn_data_put_null(@data))
end
null=(value) click to toggle source

Utility method for Qpid::Proton::Codec::Mapping

@private

# File lib/codec/data.rb, line 436
def null=(value)
  null
end
null?() click to toggle source

Checks if the current node is null.

@return [Boolean] True if the node is null.

# File lib/codec/data.rb, line 465
def null?
  Cproton.pn_data_is_null(@data)
end
object() click to toggle source

Gets the current node, based on how it was encoded.

@return [Object] The current node.

# File lib/codec/data.rb, line 455
def object
  type = self.type
  return nil if type.nil?
  type.get(data)
end
object=(object) click to toggle source

Puts an arbitrary object type.

The Data instance will determine which AMQP type is appropriate and will use that to encode the object.

@param object [Object] The value.

# File lib/codec/data.rb, line 447
def object=(object)
  Mapping.for_class(object.class).put(self, object)
end
prev() click to toggle source

Advances the current node to its previous sibling and returns its type.

If there is no previous sibling then the current node remains unchanged and nil is return.

# File lib/codec/data.rb, line 146
def prev
  return Cproton.pn_data_prev(@data) ? type : nil
end
put(value, type_code) click to toggle source

Puts a new value with the given type into the current node.

@param value [Object] The value. @param #type_code [Mapping] The value's type.

@private

# File lib/codec/data.rb, line 885
def put(value, type_code);
  type_code.put(self, value);
end
put_array(described, element_type) click to toggle source

Puts an array value.

Elements may be filled by entering the array node and putting the element values. The values must all be of the specified array element type.

If an array is described then the first child value of the array is the descriptor and may be of any type.

@param described [Boolean] True if the array is described. @param element_type [Integer] The AMQP type for each element of the array.

@example

# create an array of integer values
data = Qpid::Proton::Codec::Data.new
data.put_array(false, INT)
data.enter
data.int = 1
data.int = 2
data.int = 3
data.exit

# create a described  array of double values
data.put_array(true, DOUBLE)
data.enter
data.symbol = "array-descriptor"
data.double = 1.1
data.double = 1.2
data.double = 1.3
data.exit
# File lib/codec/data.rb, line 340
def put_array(described, element_type)
  check(Cproton.pn_data_put_array(@data, described, element_type.code))
end
put_described() click to toggle source

Puts a described value.

A described node has two children, the descriptor and the value. These are specified by entering the node and putting the desired values.

@example

data = Qpid::Proton::Codec::Data.new
data.put_described
data.enter
data.symbol = "value-descriptor"
data.string = "the value"
data.exit
# File lib/codec/data.rb, line 393
def put_described
  check(Cproton.pn_data_put_described(@data))
end
put_list() click to toggle source

Puts a list value.

Elements may be filled by entering the list node and putting element values.

@example

data = Qpid::Proton::Codec::Data.new
data.put_list
data.enter
data.int = 1
data.int = 2
data.int = 3
data.exit
# File lib/codec/data.rb, line 241
def put_list
  check(Cproton.pn_data_put_list(@data))
end
put_map() click to toggle source

Puts a map value.

Elements may be filled by entering the map node and putting alternating key/value pairs.

@example

data = Qpid::Proton::Codec::Data.new
data.put_map
data.enter
data.string = "key"
data.string = "value"
data.exit
# File lib/codec/data.rb, line 277
def put_map
  check(Cproton.pn_data_put_map(@data))
end
rewind() click to toggle source

Clears the current node and sets the parent to the root node.

Clearing the current node sets it before the first node, calling next will advance to the first node.

# File lib/codec/data.rb, line 128
def rewind
  Cproton.pn_data_rewind(@data)
end
short() click to toggle source

If the current node is a short, returns its value. Otherwise, returns a 0.

@return [Integer] The short value.

# File lib/codec/data.rb, line 550
def short
  Cproton.pn_data_get_short(@data)
end
short=(value) click to toggle source

Puts a short value.

@param value [Integer] The short value.

# File lib/codec/data.rb, line 541
def short=(value)
  check(Cproton.pn_data_put_short(@data, value))
end
string() click to toggle source

If the current node is a string, returns its value. Otherwise, it returns an empty string (“”).

@return [String] The UTF-8 encoded string.

@see binary

# File lib/codec/data.rb, line 846
def string
  Qpid::Proton::Types::UTFString.new(Cproton.pn_data_get_string(@data))
end
string=(value) click to toggle source

Puts a UTF-8 encoded string value.

NOTE: A nil value is stored as an empty string rather than as a nil.

@param value [String] The UTF-8 encoded string value.

@see binary=

# File lib/codec/data.rb, line 835
def string=(value)
  check(Cproton.pn_data_put_string(@data, value))
end
symbol() click to toggle source

If the current node is a symbol, returns its value. Otherwise, it returns an empty string (“”).

@return [String] The symbolic string value.

# File lib/codec/data.rb, line 863
def symbol
  Cproton.pn_data_get_symbol(@data)
end
symbol=(value) click to toggle source

Puts a symbolic value.

@param value [String] The symbolic string value.

# File lib/codec/data.rb, line 854
def symbol=(value)
  check(Cproton.pn_data_put_symbol(@data, value))
end
timestamp() click to toggle source

If the current node is a timestamp, returns its value. Otherwise, returns 0.

@return [Integer] The timestamp value.

# File lib/codec/data.rb, line 656
def timestamp
  Cproton.pn_data_get_timestamp(@data)
end
timestamp=(value) click to toggle source

Puts a timestamp value.

@param value [Integer] The timestamp value.

# File lib/codec/data.rb, line 646
def timestamp=(value)
  value = value.to_i if (!value.nil? && value.is_a?(Time))
  check(Cproton.pn_data_put_timestamp(@data, value))
end
to_s() click to toggle source

@private

# File lib/codec/data.rb, line 109
def to_s
  tmp = Cproton.pn_string("")
  Cproton.pn_inspect(@data, tmp)
  result = Cproton.pn_string_get(tmp)
  Cproton.pn_free(tmp)
  return result
end
type() click to toggle source

@return [Integer] The type object for the current node. @see type_code

# File lib/codec/data.rb, line 178
def type
  Mapping.for_code(type_code)
end
type_code() click to toggle source

Returns the numeric type code of the current node.

@return [Integer] The current node type. @return [nil] If there is no current node.

# File lib/codec/data.rb, line 170
def type_code
  dtype = Cproton.pn_data_type(@data)
  return (dtype == -1) ? nil : dtype
end
ubyte() click to toggle source

If the current node is an unsigned byte, returns its value. Otherwise, it returns 0.

@return [Integer] The unsigned byte value.

# File lib/codec/data.rb, line 499
def ubyte
  Cproton.pn_data_get_ubyte(@data)
end
ubyte=(value) click to toggle source

Puts an unsigned byte value.

@param value [Integer] The unsigned byte value.

# File lib/codec/data.rb, line 490
def ubyte=(value)
  check(Cproton.pn_data_put_ubyte(@data, value))
end
uint() click to toggle source

If the current node is an unsigned int, returns its value. Otherwise, returns 0.

@return [Integer] The unsigned integer value.

# File lib/codec/data.rb, line 569
def uint
  Cproton.pn_data_get_uint(@data)
end
uint=(value) click to toggle source

Puts an unsigned integer value.

@param value [Integer] the unsigned integer value

# File lib/codec/data.rb, line 558
def uint=(value)
  raise TypeError if value.nil?
  raise RangeError, "invalid uint: #{value}" if value < 0
  check(Cproton.pn_data_put_uint(@data, value))
end
ulong() click to toggle source

If the current node is an unsigned long, returns its value. Otherwise, returns 0.

@return [Integer] The unsigned long value.

# File lib/codec/data.rb, line 623
def ulong
  Cproton.pn_data_get_ulong(@data)
end
ulong=(value) click to toggle source

Puts an unsigned long value.

@param value [Integer] The unsigned long value.

# File lib/codec/data.rb, line 612
def ulong=(value)
  raise TypeError if value.nil?
  raise RangeError, "invalid ulong: #{value}" if value < 0
  check(Cproton.pn_data_put_ulong(@data, value))
end
ushort() click to toggle source

If the current node is an unsigned short, returns its value. Otherwise, it returns 0.

@return [Integer] The unsigned short value.

# File lib/codec/data.rb, line 533
def ushort
  Cproton.pn_data_get_ushort(@data)
end
ushort=(value) click to toggle source

Puts an unsigned short value.

@param value [Integer] The unsigned short value

# File lib/codec/data.rb, line 524
def ushort=(value)
  check(Cproton.pn_data_put_ushort(@data, value))
end
uuid() click to toggle source

If the current value is a UUID, returns its value. Otherwise, it returns nil.

@return [String] The string representation of the UUID.

# File lib/codec/data.rb, line 797
def uuid
  value = ""
  Cproton.pn_data_get_uuid(@data).each{|val| value += ("%02x" % val)}
  value.insert(8, "-").insert(13, "-").insert(18, "-").insert(23, "-")
end
uuid=(value) click to toggle source

Puts a UUID value.

The UUID is expected to be in the format of a string or else a 128-bit integer value.

@param value [String, Numeric] A string or numeric representation of the UUID.

@example

# set a uuid value from a string value
require 'securerandom'
@data.uuid = SecureRandom.uuid

# or
@data.uuid = "fd0289a5-8eec-4a08-9283-81d02c9d2fff"

# set a uuid value from a 128-bit value
@data.uuid = 0 # sets to 00000000-0000-0000-0000-000000000000
# File lib/codec/data.rb, line 770
def uuid=(value)
  raise ::ArgumentError, "invalid uuid: #{value}" if value.nil?

  # if the uuid that was submitted was numeric value, then translated
  # it into a hex string, otherwise assume it was a string represtation
  # and attempt to decode it
  if value.is_a? Numeric
    value = "%032x" % value
  else
    raise ::ArgumentError, "invalid uuid: #{value}" if !valid_uuid?(value)

    value = (value[0, 8]  +
             value[9, 4]  +
             value[14, 4] +
             value[19, 4] +
             value[24, 12])
  end
  bytes = []
  value.scan(/(..)/) {|v| bytes << v[0].to_i(16)}
  check(Cproton.pn_data_put_uuid(@data, bytes))
end

Private Instance Methods

check(err) click to toggle source

@private

# File lib/codec/data.rb, line 898
def check(err)
  if err < 0
    raise DataError, "[#{err}]: #{Cproton.pn_data_error(@data)}"
  else
    return err
  end
end
valid_uuid?(value) click to toggle source
# File lib/codec/data.rb, line 891
def valid_uuid?(value)
  # ensure that the UUID is in the right format
  # xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
  value =~ /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/
end