Porting C Code to Ruby June 22nd, 2007

I try to port fsplib C library to pure ruby library. What is FSP anyway? Quoting from the official site: “FSP stands for File Service Protocol. It is a very lightweight UDP based protocol for transferring files. FSP has many benefits over FTP, mainly for running anonymous archives. FSP protocol is valuable in all kinds of environments because it is one of the only TCP/IP protocols that is not aggressive about bandwidth, while still being sufficiently fault tolerant.” It is not popular option for file transferring protocol.

So why do you want to port it anyway? Because it is a good exercise for my ruby skill. I could use it as portfolio too. Another reason is I can use this as opportunity to contribute to opensource.

I decided to port it as pure ruby library rather than binding to C api because this library does need the performance but need more portability. This is second day porting the library. Here’s what I have learned so far.

The C library has this struct:
typedef struct FSP_PKT {
                        unsigned char       cmd; /* message code.             */
                        unsigned char       sum; /* message checksum.         */
                        unsigned short      key; /* message key.              */
                        unsigned short      seq; /* message sequence number.  */
                        unsigned short      len; /* number of bytes in buf 1. */
                        unsigned int        pos; /* location in the file.     */                        unsigned short     xlen; /* number of bytes in buf 2  */

                        unsigned char   buf[FSP_SPACE];   /* packet payload */
              } FSP_PKT;
How do you port this struct to ruby? Using Struct class.
FSP_PKT = Struct.new(:cmd, :sum, :key, :seq, :len, :pos, :xlen, :buf)
But that is not good enough. After reading this code, I need a better implementation:
ptr[FSP_OFFSET_CMD]=p->cmd;
    ptr[FSP_OFFSET_SUM]=0;
    *(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);

You see uint16_t stuff? Then I found the better way to implement the struct. Using Bit-Struct. I have not used the library yet. Bit-struct is designed to handle the case. From the website: “Class for packed binary data stored in ruby Strings. BitStruct accessors, generated from user declared fields, use pack/unpack to treat substrings as fields with a specified portable format.”

So I use this class to implement the struct (not yet implemented by me):
class FSP_PKT < BitStruct
         unsigned    :cmd,     2,     "message code." 
         unsigned    :sum,     2,     "message checksum." 
         unsigned    :key,      2,     "message key." 
         unsigned    :seq,      2,     "message sequence number." 
         unsigned    :len,      2,      "number of bytes in buf 1." 
         unsigned    :pos,      4,      "location in the file." 
         ..........
      end

Because I need to implement htonl and htons function in ruby, I learned about byte ordering or endianess. There is big endian and little endian. My computer (Intel) use little endian. PowerPC and network use big endian. The difference between them is little endian put lowest bit in left and biggest bit in right (right to left) and big endian put biggest bit in right and lowest bit in right (left to right). Consider this number: 28 which the binary representation is 11100 or 00011100. In little endian, this number is composed in memory like this (depending computer implementation): “00”, “11”, “01”, “00”. In big endian, it is: “00”, “01”, “11”, “00”. I use 2 bit as one block. It could be 4 bit as one block. Little endian: “1100”, “0001”. Big endian: “0001”, “1100”. Maybe there is a mistake in my explanation because I just learn about endianess today.

Leave a Reply