From: Joel VanderWerf Date: 2007-06-24T13:14:47+09:00 Subject: Re: ruby equivalent of htons or htonl Daniel Martin wrote: > akbarhome 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 # # -- vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407