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:
-
NULL
-
BOOL
-
UBYTE
-
BYTE
-
USHORT
-
SHORT
-
UINT
-
INT
-
CHAR
-
ULONG
-
LONG
-
TIMESTAMP
-
FLOAT
-
DOUBLE
-
DECIMAL32
-
DECIMAL64
-
DECIMAL128
-
UUID
-
BINARY
-
SYMBOL
The following types of compound values are supported:
-
DESCRIBED
-
ARRAY
-
LIST
Public Class Methods
@private
# File lib/codec/data.rb, line 102 def self.finalize!(data, free) proc { Cproton.pn_data_free(data) if free } end
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
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
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
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
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
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
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
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
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
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
Clears the object.
# File lib/codec/data.rb, line 119 def clear Cproton.pn_data_clear(@data) end
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
@private
# File lib/codec/data.rb, line 374 def get_array ::Array.proton_get(self) end
@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
@private
# File lib/codec/data.rb, line 304 def get_map ::Hash.proton_data_get(self) end
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
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
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
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
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
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
Puts a null value.
# File lib/codec/data.rb, line 428 def null check(Cproton.pn_data_put_null(@data)) end
Utility method for Qpid::Proton::Codec::Mapping
@private
# File lib/codec/data.rb, line 436 def null=(value) null end
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
@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
@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
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
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
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
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
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
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
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
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
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
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
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
@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
# 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