Class: XS::Message

Inherits:
Object
  • Object
show all
Defined in:
lib/ffi-rxs/message.rb

Overview

The factory constructor optionally takes a string as an argument. It will copy this string to native memory in preparation for transmission. So, don’t pass a string unless you intend to send it. Internally it calls #copy_in_string.

Call #close to release buffers when you are done with the data.

(This class is not really zero-copy. Ruby makes this near impossible since Ruby objects can be relocated in memory by the GC at any time. There is no way to peg them to native memory or have them use non-movable native memory as backing store.)

Message represents ruby equivalent of the xs_msg_t C struct. Access the underlying memory buffer and the buffer size using the #data and #size methods respectively.

It is recommended that this class be composed inside another class for access to the underlying buffer. The outer wrapper class can provide nice accessors for the information in the data buffer; a clever implementation can probably lazily encode/decode the data buffer on demand. Lots of protocols send more information than is strictly necessary, so only decode (copy from the 0mq buffer to Ruby) that which is necessary.

When you are done using a received message object, call #close to release the associated buffers.

Examples:

received_message = Message.create
if received_message
  rc = socket.recvmsg(received_message)
  if XS::Util.resultcode_ok?(rc)
    puts "Message contained: #{received_message.copy_out_string}"
  else
    STDERR.puts "Error when receiving message: #{XS::Util.error_string}"
  end

Define a custom layout for the data sent between Crossroads peers

class MyMessage
  class Layout < FFI::Struct
    layout :value1, :uint8,
           :value2, :uint64,
           :value3, :uint32,
           :value4, [:char, 30]
  end

  def initialize msg_struct = nil
    if msg_struct
      @msg_t = msg_struct
      @data = Layout.new(@msg_t.data)
    else
      @pointer = FFI::MemoryPointer.new :byte, Layout.size, true
      @data = Layout.new @pointer
    end
  end

  def size() @size = @msg_t.size; end

  def value1
    @data[:value1]
  end

  def value4
    @data[:value4].to_ptr.read_string
  end

  def value1=(val)
    @data[:value1] = val
  end

  def create_sendable_message
    msg = Message.new
    msg.copy_in_bytes @pointer, Layout.size
  end
end

message = Message.new
successful_read = socket.recv message
message = MyMessage.new message if successful_read
puts "value1 is #{message.value1}"

Direct Known Subclasses

ManagedMessage

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(message = nil) ⇒ Message

Initialize object

Parameters:

  • message (optional) (defaults to: nil)


101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/ffi-rxs/message.rb', line 101

def initialize message = nil
  # allocate our own pointer so that we can tell it to *not* zero out
  # the memory; it's pointless work since the library is going to
  # overwrite it anyway.
  @pointer = FFI::MemoryPointer.new Message.msg_size, 1, false

  if message
    copy_in_string message
  else
    # initialize an empty message structure to receive a message
    result_code = LibXS.xs_msg_init @pointer
    raise unless Util.resultcode_ok?(result_code)
  end
end

Class Method Details

.create(message = nil) ⇒ Message

Recommended way to create a standard message.

Parameters:

  • message (optional) (defaults to: nil)

Returns:

  • (Message)

    upon success

  • nil when allocation fails



94
95
96
# File 'lib/ffi-rxs/message.rb', line 94

def self.create message = nil
  new(message) rescue nil
end

.msg_sizeObject

Store the message size



216
# File 'lib/ffi-rxs/message.rb', line 216

def self.msg_size() @msg_size; end

Instance Method Details

#addressObject Also known as: pointer

Provides the memory address of the xs_msg_t struct. Used mostly for passing to other methods accessing the underlying library that require a real data address.



149
150
151
# File 'lib/ffi-rxs/message.rb', line 149

def address
  @pointer
end

#closeObject

Manually release the message struct and its associated data buffer.

Only releases the buffer a single time. Subsequent calls are no ops.

Returns:

  • 0 if successful

  • -1 if unsuccessful



200
201
202
203
204
205
206
207
208
209
# File 'lib/ffi-rxs/message.rb', line 200

def close
  rc = 0
  
  if @pointer
    rc = LibXS.xs_msg_close @pointer
    @pointer = nil
  end
  
  rc
end

#copy(source) ⇒ Object

Copy content of message to another message

Parameters:

  • source


157
158
159
# File 'lib/ffi-rxs/message.rb', line 157

def copy source
  LibXS.xs_msg_copy @pointer, source
end

#copy_in_bytes(bytes, len) ⇒ Object

Makes a copy of len bytes from the ruby string bytes. Library handles deallocation of the native memory buffer.

Can only be initialized via #copy_in_string or #copy_in_bytes once.

Parameters:

  • bytes
  • length


135
136
137
138
139
140
141
142
143
144
# File 'lib/ffi-rxs/message.rb', line 135

def copy_in_bytes bytes, len
  data_buffer = LibC.malloc len
  # writes the exact number of bytes, no null byte to terminate string
  data_buffer.write_string bytes, len

  # use libC to call free on the data buffer; earlier versions used an
  # FFI::Function here that called back into Ruby, but Rubinius won't 
  # support that and there are issues with the other runtimes too
  LibXS.xs_msg_init_data @pointer, data_buffer, len, LibC::Free, nil
end

#copy_in_string(string) ⇒ Object

Makes a copy of the ruby string into a native memory buffer so that libxs can send it. The underlying library will handle deallocation of the native memory buffer.

Can only be initialized via #copy_in_string or #copy_in_bytes once.

Parameters:

  • string


123
124
125
126
# File 'lib/ffi-rxs/message.rb', line 123

def copy_in_string string
  string_size = string.respond_to?(:bytesize) ? string.bytesize : string.size
  copy_in_bytes string, string_size if string
end

#copy_out_stringObject

Returns the data buffer as a string.

Note: If this is binary data, it won’t print very prettily.

Returns:

  • string



188
189
190
# File 'lib/ffi-rxs/message.rb', line 188

def copy_out_string
  data.read_string(size)
end

#dataObject

Returns a pointer to the data buffer. This pointer should never be freed. It will automatically be freed when the message object goes out of scope and gets garbage collected.

Returns:

  • pointer



179
180
181
# File 'lib/ffi-rxs/message.rb', line 179

def data
  LibXS.xs_msg_data @pointer
end

#move(source) ⇒ Object

Move content of message to another message

Parameters:

  • source


164
165
166
# File 'lib/ffi-rxs/message.rb', line 164

def move source
  LibXS.xs_msg_move @pointer, source
end

#sizeObject

Provides the size of the data buffer for this xs_msg_t C struct.



169
170
171
# File 'lib/ffi-rxs/message.rb', line 169

def size
  LibXS.xs_msg_size @pointer
end