<commit_before>
----------------------------------------------------------------------------
--  message.lua - Message base class
--
--  Created: Mon Jul 26 19:50:16 2010 (at Intel Research, Pittsburgh)
--  License: BSD, cf. LICENSE file of roslua
--  Copyright  2010  <NAME> [www.niemueller.de]
--             2010  Carnegie Mellon University
--             2010  Intel Research Pittsburgh
----------------------------------------------------------------------------

--- ROS message representation.
-- This class incorporates a ROS message that can be sent and received over
-- a TCPROS connection. A message is created via the MsgSpec:instantiate()
-- method.
-- <br /><br />
-- A message has two important fields. One is the spec field. It contains a
-- reference a a MsgSpec instance describing this message. Further there is a
-- field values which is a dict table. It contains entries for the names of
-- the message fields storing the appropriate.
-- The values array has to be filled appropriately before sending a message,
-- and on deserializing a received message it will contain the received values.
-- @copyright <NAME>, Carnegie Mellon University, Intel Research Pittsburgh
-- @release Released under BSD license
module("roslua.message", package.seeall)

require("roslua")
require("roslua.struct")

Message = { spec=nil }

--- Constructor.
-- @param spec message specification
function Message:new(spec, no_prefill)
   local o = {}
   setmetatable(o, self)
   self.__index = self

   o.spec   = spec
   assert(o.spec, "Message specification instance missing")

   o.values = {}

   if not no_prefill then o:prefill() end

   return o
end


function Message:prefill()
   for _, f in ipairs(self.spec.fields) do
      if not self.values[f.name] then
	 if f.is_array then -- just assume empty array
	    self.values[f.name] = {}
	 elseif f.is_builtin then -- set default builtin value
	    self.values[f.name] = self.default_values[f.type]
	 else -- generate new complex message with no values set to trigger defaults
	    self.values[f.name] = f.spec:instantiate()
	 end
      end
   end
end

-- (internal) table of default values for built-in types
Message.default_values = {
   int8     = 0,       uint8   = 0,
   int16    = 0,       uint16  = 0,
   int32    = 0,       uint32  = 0,
   int64    = 0,       uint64  = 0,
   float32  = 0,       float64 = 0,
   char     = 0,       byte    = 0,
   duration = {0, 0},  time    = {0, 0},
   string   = "",      array   = {}
}

-- (internal) table of formats for built-in types
Message.builtin_formats = {
   int8     = "i1",    uint8   = "I1",
   int16    = "i2",    uint16  = "I2",
   int32    = "i4",    uint32  = "I4",
   int64    = "i8",    uint64  = "I8",
   float32  = "f",     float64 = "d",
   char     = "i1",    byte    = "I1",
   duration = "i4i4",  time    = "I4I4",
   string   = "i4c0",  array   = "I4"
}


--- Deserialize received message.
-- This will deserialize the message according to the message specification.
-- Not that it is the users obligation to make sure that the buffer is correctly
-- typed for the message, especially that the buffer has the appropriate size.
-- @param buffer buffer that contains the message as read from the TCPROS connection
-- @param i Index from where to start parsing in the buffer, optional argument
-- that defaults to 1
function Message:deserialize(buffer)
   local format = self:format_string(buffer, 1)
   local values = {struct.unpack(format, buffer)}
   self:read_values(values)
end


--- Generate format string.
-- The format string is based on the base format of the message specification,
-- but takes into account actual array sizes, replicating array parts as
-- necessary.
-- @param buffer incoming buffer to be deserialized
-- @param i index from where to start reading buffer
-- @param farray base format array from message spec, if nil the one from
-- internal spec is used (recursion parameter)
-- @param format prefix if nil set to little endian and 1 byte alignment
-- (recursion parameter)
function Message:format_string(buffer, i, farray, prefix)
   local rv = prefix or "<!1"
   local farray = farray or self.spec.base_farray
   for j, f in ipairs(farray) do
      if type(f) == "string" then
	 rv = rv .. f<commit_msg>Message deserialization: split long format strings<commit_after>
----------------------------------------------------------------------------
--  message.lua - Message base class
--
--  Created: Mon Jul 26 19:50:16 2010 (at Intel Research, Pittsburgh)
--  License: BSD, cf. LICENSE file of roslua
--  Copyright  2010  <NAME> [www.niemueller.de]
--             2010  Carnegie Mellon University
--             2010  Intel Research Pittsburgh
----------------------------------------------------------------------------

--- ROS message representation.
-- This class incorporates a ROS message that can be sent and received over
-- a TCPROS connection. A message is created via the MsgSpec:instantiate()
-- method.
-- <br /><br />
-- A message has two important fields. One is the spec field. It contains a
-- reference a a MsgSpec instance describing this message. Further there is a
-- field values which is a dict table. It contains entries for the names of
-- the message fields storing the appropriate.
-- The values array has to be filled appropriately before sending a message,
-- and on deserializing a received message it will contain the received values.
-- @copyright <NAME>, Carnegie Mellon University, Intel Research Pittsburgh
-- @release Released under BSD license
module("roslua.message", package.seeall)

require("roslua")
require("roslua.struct")

Message = { spec=nil }

--- Constructor.
-- @param spec message specification
function Message:new(spec, no_prefill)
   local o = {}
   setmetatable(o, self)
   self.__index = self

   o.spec   = spec
   assert(o.spec, "Message specification instance missing")

   o.values = {}

   if not no_prefill then o:prefill() end

   return o
end


function Message:prefill()
   for _, f in ipairs(self.spec.fields) do
      if not self.values[f.name] then
	 if f.is_array then -- just assume empty array
	    self.values[f.name] = {}
	 elseif f.is_builtin then -- set default builtin value
	    self.values[f.name] = self.default_values[f.type]
	 else -- generate new complex message with no values set to trigger defaults
	    self.values[f.name] = f.spec:instantiate()
	 end
      end
   end
end

-- (internal) table of default values for built-in types
Message.default_values = {
   int8     = 0,       uint8   = 0,
   int16    = 0,       uint16  = 0,
   int32    = 0,       uint32  = 0,
   int64    = 0,       uint64  = 0,
   float32  = 0,       float64 = 0,
   char     = 0,       byte    = 0,
   duration = {0, 0},  time    = {0, 0},
   string   = "",      array   = {}
}

-- (internal) table of formats for built-in types
Message.builtin_formats = {
   int8     = "i1",    uint8   = "I1",
   int16    = "i2",    uint16  = "I2",
   int32    = "i4",    uint32  = "I4",
   int64    = "i8",    uint64  = "I8",
   float32  = "f",     float64 = "d",
   char     = "i1",    byte    = "I1",
   duration = "i4i4",  time    = "I4I4",
   string   = "i4c0",  array   = "I4"
}


--- Deserialize received message.
-- This will deserialize the message according to the message specification.
-- Not that it is the users obligation to make sure that the buffer is correctly
-- typed for the message, especially that the buffer has the appropriate size.
-- @param buffer buffer that contains the message as read from the TCPROS connection
-- @param i Index from where to start parsing in the buffer, optional argument
-- that defaults to 1
function Message:deserialize(buffer)
   local format = self:format_string(buffer, 1, self.farray, "")
   local values = {}
   local l = 0
   local i = 1
   for s in self:split_format(format) do
      l = l + #s
      local tmpval = {struct.unpack("<!1" .. s, buffer, i)}
      i = tmpval[#tmpval]
      table.remove(tmpval)
      for _, v in ipairs(tmpval) do
	 table.insert(values, v)
      end
   end
   self:read_values(values)
end

--- Format splitting iterator.
-- Splits the struct format after 200 characters (202 if a string definition
-- would be split otherwise).
-- @param format format to split
-- @return iterator function
function Message:split_format(format)
   local lenformat = #format
   local l = 1
   return function()
	     if l < lenformat then
		local lread = 200
		if lenformat - l < lread then
		   lread = lenformat - l + 1
		end
		local f = format:sub(l, l+lread-1)
		l = l + lread

		if format:sub(l, l+1) == "c0" then
		   f = f .. format:sub(l, l+1)
		   l = l + 2
		end
		return f
	     end
	  end
end


--- Generate format string.
-- The format string is based on the base format of the message specification,
-- but takes into account actual array sizes, replicating array parts as
-- necessary.
-- @param buffer incoming buffer to be deserialized
-- @param i index from where to start reading buffer
-- @param farray base format array from message spec, if nil the one from
-- internal spec is used (recursion parameter)
-- @param format prefix if nil set to little endian and 1 byte alignment
-- (recursion parameter)
function Message:format_string(buffer, i, farray, prefix)
   local rv = prefix or "<!1"
   local farray = farray or self.spec.base_farray
   for j, f in ipairs(farray) do
      if type(f) == "string" then
	 rv = rv .. f
{'fraction_non_alphanumeric': 0.12918304472537978, 'fraction_numerical': 0.02262828003824498, 'mean_word_length': 2.461029411764706, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3477975', 'n_tokens_mistral': 2919, 'n_tokens_neox': 2713, 'n_words': 1110}