Writing XDR data to an expanding buffer

August 13th, 2007

The other day I was implementing support for XDR insertion/extraction in the C++/Tree mapping. XDR is a binary representation format that allows you to store, move, and then extract your data without worrying about word sizes (32 vs 64 bit), endian-ness, etc. XDR is available out of the box on pretty much every UNIX and GNU/Linux system as part of Sun RPC.

To test the performance of my implementation I was first serializing a large object model to a memory buffer and then deserializing it from that buffer. You can easily create an XDR stream to read/write the data from/to a fixed-size memory buffer (xdrmem_create) or a standard I/O stream (xdrstdio_create). There is also the xdrrec_create function which supports a record-oriented serialization as well as an abstract, callback-based underlying buffer management. This function is the only option for serializing to a dynamically-expanding buffer short of creating your own XDR stream. Unfortunately there aren’t many examples that show how to use it so I had to figure out the correct usage myself.

The following code fragment shows how to read/write XDR data using the xdrrec_create function. We use std::vector<char> as a buffer:

#include <vector>
#include <cstring> // std::memcpy
#include <iostream>
 
#include <rpc/xdr.h>
 
using namespace std;
 
typedef vector<char> buffer;
 
extern "C" int
overflow (char* p, char* data, int n)
{
  buffer* buf (reinterpret_cast<buffer*> (p));
 
  size_t size (buf->size ());
  buf->resize (size + n);
 
  memcpy (buf->data () + size, data, n);
 
  return n;
}
 
struct underflow_info
{
  buffer* buf;
  size_t pos;
};
 
extern "C" int
underflow (char* p, char* data, int n)
{
  underflow_info* ui (reinterpret_cast<underflow_info*> (p));
 
  size_t size (ui->buf->size () - ui->pos);
  n = size > n ? n : size;
 
  memcpy (data, ui->buf->data () + ui->pos, n);
  ui->pos += n;
 
  return n;
}
 
int
main ()
{
  buffer buf;
 
  // Serialize.
  //
  XDR oxdr;
  xdrrec_create (&oxdr,
                 0,
                 0,
                 reinterpret_cast<char*> (&buf),
                 0,
                 &overflow);
  oxdr.x_op = XDR_ENCODE;
 
  unsigned int i (10);
  xdr_uint32_t (&oxdr, &i);
 
  xdrrec_endofrecord (&oxdr, true); // flush the data.
  xdr_destroy (&oxdr);
 
  cerr << "size: " << buf.size () << endl;
 
  // Deserialize.
  //
  underflow_info ui;
  ui.buf = &buf;
  ui.pos = 0;
 
  XDR ixdr;
  xdrrec_create (&ixdr,
                 0,
                 0,
                 reinterpret_cast<char*> (&ui),
                 &underflow,
                 0);
  ixdr.x_op = XDR_DECODE;
  xdrrec_skiprecord (&ixdr);
 
  i = 0;
  xdr_uint32_t (&ixdr, &i);
 
  xdr_destroy (&ixdr);
 
  cerr << "i: " << i << endl;
}

The most non-obvious part in this code is the call to xdrrec_skiprecord.

How to market to smart people

January 30th, 2007

It should be pretty obvious, but a lot of people forget this important first step: your product shouldn’t suck. Otherwise you are wasting your time. For smart people the product should be at least good, better yet—great. It should be deep and flexible. When you plan to implement a feature, don’t just cover use-cases that you’ve managed to come up with; make it flexible so if a smart person thought of a new scenario, it will still be covered.

But don’t make it everything for all people. The product should be focused and simple enough so that the development converges and you can spend time polishing and perfecting it.

Assuming you have a product you feel proud about, how do you market it to smart people? Let’s first consider a few intrinsic properties that make your product more attractive to smart people.

Make it open. Make your product as open as you possibly can, the ideal being open-source. Publish protocols and provide SDKs. Allow smart people to use your product in ways you haven’t thought of. Another important thing is to make your support system open as well. This will allow prospective customers to see what kind of problems others are having with your product and how you handle them.

Facilitate test-driving. Make it as easy as possible to try your product. That means no crippled evaluation versions or long online registration forms. The reality is that most of today’s products suck, and that’s what a smart person will assume about your product until proven otherwise. The ideal is a direct link to the package: just install using your platform’s native method and you are all set. Now we can move to the marketing part.

Don’t deliver – allow discovery. Don’t force information on smart people. Instead publish it in relevant places and allow smart people to find it when they need a product like yours. This approach has an advantage of also covering search engines. When you do the right thing everything falls into place.

Don’t hype. The information you provide should be useful. Present honest differentiation of your product compared to alternatives. Don’t use meaningless terms like fast, scalable, reliable, or easy to use unless it is clear (e.g., a well know fact or from your competitor’s website) that the alternatives are slow, do not scale, unreliable or hard to use. Things like open source and cross-platform are good differentiators if you are competing against proprietary products that work only on Windows.

Scrap online advertising. Online advertising has discredited itself to the point that most smart people automatically block or ignore ads.

Don’t bash competition, especially on their turf. It is your word versus theirs. Bashing competition on their own turf (forums, mailing lists, etc.) is especially ill-advised because you are telling people who are already invested in your competitor’s product that they’ve made a mistake.

Prove your product is great. The only way you can do this is with real customer case studies and testimonials. Don’t just list your customer names. Tell how they use your product and how it helped them solve their problems. The best testimonials are the ones that have extra credibility of a voluntary post on a mailing list or a forum.

Prove you are the best. You need to have a great reputation besides a great product. Publishing quality articles that share your knowledge and wisdom. This will position you as an expert in the field. But don’t try to make it a “big ad” for your product. Instead concentrate on how the overall technology will help smart people, whether they choose your product or your competitor’s.

Statically-linking libstdc++ on AIX

December 10th, 2006

Yesterday I spent the whole day trying to build a statically-linked binary of XSD for AIX. I have done this for numerous other platforms but on AIX the C++ exception handling stopped working once I linked libstdc++ in statically. I was lucky to detect this problem at all; the application appears to work just fine as long as there is no throw on the execution path. The fix is to also link libsupc++.a in:

g++ -static-libgcc ... -Wl,-bstatic -lstdc++ -Wl,-bdynamic \
-lsupc++

If all you are looking for is the fix, then you can stop reading here. Read on if you want to know why we need libsupc++.a. When your application is linked to a number of shared libraries, it is important that they all use the same exception handling runtime to allow exceptions to propagate across library boundaries. To ensure this, GCC places the exception handling runtime into a shared library called libgcc_s.so (libgcc.a on AIX) which is then used by all shared C++ libraries and executables, including libstdc++.so.

The -static-libgcc option instructs GCC to use a static equivalent of libgcc_s.so called libgcc_eh.a with the difference being that some of the exception handling code found in libgcc_s.so is placed into the static libstdc++.a (tell me if you know why). In other words, shared libstdc++.so and static libstdc++.a have a different amount of code in them. This fact is important to understand why things fail on AIX.

The second part of the problem is an eccentric shared library architecture implemented on AIX. In short, both shared libraries and static archives on AIX use the same file suffix—.a. In fact, they are both archives. In such an archive there could be one or more shared object (which is just a normal XCOFF file with the SHROBJ bit set) or normal object files. Furthermore, you can link a shared library (archive with shared objects) statically. That’s exactly what happens when you instruct g++ to link libstdc++.a statically by using the -bstatic linker option.

We can see the cause of the problem clearly now: we are linking shared libstdc++.a statically but it is missing some of the exception handling code that a proper static libstdc++.a would have. The libsupc++.a is a subset of libstdc++.a. It provides core language support without STL and streams. It also includes the exception handling code we need. That’s why linking this library fixes the problem.