Daniel Martin wrote:
> akbarhome <akbarhome / gmail.com> writes:
> 
>> I try to port this c code:
>> *(uint16_t *)(ptr+FSP_OFFSET_KEY)=htons(p->key);
>>     *(uint16_t *)(ptr+FSP_OFFSET_SEQ)=htons(p->seq);
>>     *(uint16_t *)(ptr+FSP_OFFSET_LEN)=htons(p->len);
>>     *(uint32_t *)(ptr+FSP_OFFSET_POS)=htonl(p->pos);
>>
>> About uint16_t, I have bit-struct library. But htons messed up my
>> head.
> 
> You are thinking about too low a level of porting.
> 
> Look, what is this code doing?  It is packing up a structure, probably
> to be sent over the wire.  So let's just do that part, okay?  Don't
> try to separately do the htonl conversion and the structure-packing.
> There's a reason the function isn't in the ruby standard library: the
> only time it's needed is when you're packing things up anyway, so it's
> built into pack and unpack.
> 
> Here's a rough translation of what I think you're trying to write:
> 
> # A translation of fsp_pkt_write from 
> # http://csourcesearch.net/package/gftp/2.0.18/gftp-2.0.18/lib/fsplib/fsplib.c
> 
> # Note that in ruby we return the new string, and don't worry about
> # preallocating a buffer.
> 
> # Also, I'd rename this method to something like "fsp_pkt_make" since
> # it doesn't really *write* the data to the output, but that's what
> # the function is called in C, so...
> 
> def fsp_pkt_write(fsp_pkt)
> 
>   fsp_string = [fsp_pkt.cmd, 0, fsp_pkt.key,
>                 fsp_pkt.seq, fsp_pkt.len, fsp_pkt.pos].pack("CCnnnN")
> 
>   # I assume that in the ruby version, p.buf contains both the data
>   # block and the "extra data" block 
> 
>   fsp_string += fsp_pkt.buf
> 
>   checksum = 0
>   fsp_string.each_byte {|i| checksum += i+1}
>   # Note: adding 1 above at each byte is equivalent to adding the length
>   fsp_string[1] = (checksum & 0xFF)
>   return fsp_string
> 
> end
> 
> Now, wasn't that easier than hauling out bitstruct to get a
> line-by-line translation?

bit-struct wouldn't be useful for a line-by-line translation.

The fsp_pkt_write method is nice and simple. (The only problem is that 
checksum&0xFF is not the same as checksum + (checksum >> 8), but maybe 
there is something about fsp packets that I'm missing here.)

If you do want to use bit-struct, see below. One disadvantage with 
bit-struct is that you might be tempted to assign to members after 
initializing the struct and calculating the checksum, which would 
invalidate the checksum. Having a single method that does all of the 
calculations and gives you a string prevents this temptation. YMMV.

require 'bit-struct'

class FSPPacket < BitStruct
   # This is already the default:
   #default_options :endian => :network

   unsigned :cmd,     8
   unsigned :sum,     8

   unsigned :key,    16
   unsigned :seq,    16
   unsigned :len,    16
   unsigned :pos,    32

   rest :buf

   def initialize(*)
     super

     self.len = buf.length # ?? is this right?

     self.sum = 0
     checksum = length
     each_byte { |i| checksum += i }
     self.sum = checksum + (checksum >> 8)
   end
end

fsp = FSPPacket.new do |pkt|
   pkt.cmd = 123
   pkt.key = 42
   pkt.buf = "foo bar"
   # ...
   # all changes within this block will be reflected in the sum and len
end

p fsp # #<FSPPacket cmd=123, sum=91, key=42, seq=0, len=7, pos=0, 
buf="foo bar">


-- 
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407