Xerces-C++ 2.8.0 released

September 2nd, 2007

After two years of development, Xerces-C++ 2.8.0 is finally out. This release doesn’t add any new features compared to 2.7.0 but is rather focused on the bug fixes, optimizations, and build system improvements (for new features, 3.0.0 is underway). 2.8.0 is interface-compatible with 2.7.0 which means all you will need to do to take advantage of all the improvements is to recompile your applications. For the complete list of changes in this version refer to the official Release Information page on the project’s website. I am especially interested in all the XML Schema fixes that allow Xerces-C++ 2.8.0 to handle widely-used schemas and standards such as Geography Markup Language (GML) and COLLADA (COLLAborative Design Activity). In this post I am going to discuss a number of user-visible improvements that many Xerces-C++ users may want to know more about.

Compared to the previous release, Xerces-C++ 2.8.0 comes with a wide range of precompiled libraries (total 23) for various CPU architectures, operation systems, and C++ compilers. For most platforms 32 bit and 64 bit versions are provided. Note also that while the libraries are built using specific C++ compiler versions, most of them will also work with newer versions of the same compilers. For example, libraries built with GCC 3.4.x will also work with GCC 4.0.x, 4.1.x, and 4.2.x. Similarly, libraries built with Sun C++ 5.7 (Studio 10) will work with Sun C++ 5.8. For HP-UX on PA-RISC two versions of libraries are provided: one built in the “Classic” mode (-AP) and the other in the “Standard C++” mode (-AA -mt). The latter version’s archive has the _AA suffix. Also new in this release are the 64 bit Windows libraries built with Visual C++ 8.0 (2005).

On the source code level, there are three user-visible changes that I would like to cover in more detail. First, the XML to DOM parsing code was optimized with the speed gain ranging between 25-30%, depending on the XML documents used. The SAX2 parser was also improved to allocate additional memory only if the existing buffers cannot be reused. Overall, a statically-linked (see below) Xerces-C++ 2.8.0 library on GNU/Linux parses XML to DOM about 40% faster than a dynamically-linked 2.7.0 thanks to various code optimizations as well as the default optimization level change from -O to -O2 for GCC on GNU/Linux.

The second source-level change that may affect your applications is the exponential growth of memory blocks implemented in the DOM heap. Now the size of memory blocks that the DOM heap allocates at a time grows from 16KB to 128KB as the document requires more memory (in the previous versions that size was fixed at 64KB). This change will help applications with a large number of small XML documents as well as applications that handle very large documents.

Finally, two important bugs have been fixed in the DOM cloning and importing logic. First, when the complete DOM document is being cloned, the NODE_CLONED notification is sent to each node’s user data handler. This allows you to copy the user data into the new document. Second, type information (such as PSVI) that may be associated with DOM nodes is now properly copied when nodes are cloned or imported.

The build system in Xerces-C++ 2.8.0 has also been improved in a number of ways: The Visual Studio 8.0 (2005) project and solution files with support for the 64 bit builds now come with the Xerces-C++ distribution. GCC is now supported on HP-UX and AIX in addition to GNU/Linux and Solaris. The build system now automatically detects aCC3 and aCC6 on HP-UX as well as passes the correct 64 bit options to Sun C++ on SPARC and x86-64.

Furthermore, a new option, -s, was added to the runConfigure script that instructs the build system to build static archives (libxerces-c.a) instead of shared libraries. The code for the static archives is compiled without positions-independent options and therefore is faster than the shared library version.

The compilation itself can now be performed in verbose mode which is useful when you want to see the exact options that are passed to the compiler during build. To trigger verbose mode, add VERBOSE=1 to the make command line, for example:

make VERBOSE=1

Xerces-C++ 2.8.0 automatically detects necessary 64 bit options for most of the platforms and compilers that it supports. For GCC, however, it is not possible because the exact options depend on the CPU architecture and GCC version. GCC on most 32 bit GNU/Linux distributions produces 32 bit code and on 64 bit distributions—64 bit code. If GCC on your system does not generate the desired code by default, you will need to specify additional compiler and linker options using the runConfigure -z and -l options. For the exact GCC options that will switch the compiler into the desired mode on your architecture, consult GCC documentation. For the x86-64, PowerPC, and SPARC architectures these options are -m64 (64 bit mode) and -m32 (32 bit mode).

For example, to build 32 bit Xerces-C++ libraries for x86 architecture on a 64 bit GNU/Linux system that by default produces 64 bit code, you will need to use the following runConfigure options:

runConfigure -p linux -c gcc -x g++ -z -m32 -l -m32

GCC on Solaris for both x86-64 and SPARC architectures by default generates 32 bit code. To build 64 bit Xerces-C++ libraries with GCC on Solaris, use the following runConfigure options:

runConfigure -p solaris -c gcc -x g++ -b 64 -z -m64 -l -m64

As another example, to build 32 bit universal libraries for Mac OS X use the following runConfigure options:

runConfigure -p macosx -c gcc -x g++ -z -arch -z i386 \
-z -arch -z ppc -l -arch -l i386 -l -arch -l ppc

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.