Marcelo Zabani's coding blog

C# Interop with C bitfields

Advertisements

Recently I have been writing a small ASP.NET module with Mono for Nginx. I know there is a FastCGI module for hosting ASP.NET with Mono, but I’m doing this mostly with the purpose of learning.

What I am writing is not a FastCGI module, but rather one that embeds Mono within Nginx, working far more closely to the webserver than the FastCGI module probably can.

One of the most interesting things in all this is doing Interop between managed C# code and unmanaged C code. Just to illustrate, one of the C structs found in Nginx I have to deal with is this:

ngx_buf_s from https://github.com/nginx/nginx/blob/master/src/core/ngx_buf.h:


struct ngx_buf_s {
  u_char *pos;
  u_char *last;
  off_t file_pos;
  off_t file_last;

  u_char *start; /* start of buffer */
  u_char *end; /* end of buffer */
  ngx_buf_tag_t tag;
  ngx_file_t *file;
  ngx_buf_t *shadow;

  /* the buf's content could be changed */
  unsigned temporary:1;

  /*
  * the buf's content is in a memory cache or in a read only memory
  * and must not be changed
  */
  unsigned memory:1;

  /* the buf's content is mmap()ed and must not be changed */
  unsigned mmap:1;

  unsigned recycled:1;
  unsigned in_file:1;
  unsigned flush:1;
  unsigned sync:1;
  unsigned last_buf:1;
  unsigned last_in_chain:1;

  unsigned last_shadow:1;
  unsigned temp_file:1;
  /* STUB */ int num;
};
The problem is how to write a struct in C# that possesses the same memory layout as the struct above. The answer is actually quite simple: mostly, it is not possible.
The major problem lies in the bitfields: there is a lot of room to compilers in the C standard as to how they should layout bitfields in memory. How do we deal with this in a manner that is not compiler dependant? We leave the setting and getting of those bits to unmanaged code. In the end, this is what I got in C#:

[StructLayout(LayoutKind.Sequential)]
public struct NginxResponseBuffer
{
   #region Internal calls to set and read data from the struct
   [MethodImpl(MethodImplOptions.InternalCall)]
   private static extern void SetInMemoryBuf(ref NginxResponseBuffer buf, bool flag);

   [MethodImpl(MethodImplOptions.InternalCall)]
   private static extern void SetLastBuf(ref NginxResponseBuffer buf, bool flag);
   #endregion

   public IntPtr pos;
   public IntPtr last;
   public long file_pos;
   public long file_last;

   public IntPtr start;
   public IntPtr end;
   public IntPtr tag;
   public IntPtr file;
   public IntPtr shadow;

   // There are eleven bits that compose a bitfield from now on.
   // Since the C standard does not guarantee how these will be layed out in memory,
   // it is better to just have a 32 bits property here (11 bits will hopefully be padded to 32 bits).
   // After that, getter and setter methods will invoke unmanaged code that knows how to deal with these fields.
   // Disable unused field warning
   #pragma warning disable 0169
   private readonly byte b1, b2, b3, b4;
   #pragma warning restore 0169

   public bool Memory {
     set
     {
       SetInMemoryBuf(ref this, value);
     }
   }
   public bool LastBuf {
     set
     {
       SetLastBuf(ref this, value);
     }
   }

   public int num;
}

The internal methods are mapped to call unmanaged code that will set the bits accordingly (I mapped them using mono_add_internal_call, but you can also do this with the DllImportAttribute if you are on Windows or if you are not embedding mono), and the 4 bytes should never be used, even inside the code in the struct itself. Although I could have specified an explicit layout for the struct to skip these bytes without declaring them, I would then sacrifice 32bit/64bit portability, since pointers could assume different sizes. These are the methods that do the setting on the unmanaged side (please note that ngx_buf_t is just a typedef’d ngx_buf_s):


void set_in_memory_buf(ngx_buf_t *buf, bool flag)  {
   buf->memory = flag ? 1 : 0;
}

void set_last_buf(ngx_buf_t *buf, bool flag)  {
   buf->last_buf = flag ? 1 : 0;
}

I only added setters to two fields – Memory and LastBuf – because these are the only two I need right now. So far, getters aren’t needed for any of those.
I will keep you posted as to how the development of this module for Nginx goes, posting something interesting now and then. I still haven’t got it working, as apparently the call to HttpRuntime.ProcessRequest never returns.

Advertisements

Advertisements