libpsyc is a PSYC library in C which implements core aspects of PSYC, useful for all kinds of clients and servers including psyced.

Team:Core is in charge of this.


Currently libpsyc provides:

Future options:

  • various helper functions

libpsyc is designed to compile in standard C environments and in particular with dietlibc.

You can git it from git://git.psyc.eu/libpsyc

See also libpsyc's Ohloh Page and give us a five star ranking!  :-)

Contents

Rationale

  1. Storage must not happen within the library. It shall not allocate memory and it shall not make restrictions on memory slots that are passed to it. All memory changes are passed back up to higher languages since LPC is likely to store it in a completely different way than Perl, Python or C++. We need smart return values or callbacks for changes in variables and complete packets.
  2. The parser does not need to be optimized to handle incomplete packets unless they come with a content-length. Short packets normally shouldn't arrive incomplete. Nevermind, libpsyc has been checked to handle any kind of incomplete packets...
  3. If an error happens, the user of the API should be able to roll back any state changes we informed him about for that packet. It's dangerous to ignore this aspect as it can cause unpredictable bugs. libpsyc returns tables of modifiers so you can apply them to your state storage after ensuring the packet is valid in its entirety.

Be also aware of the distinction in vocabulary between the parser state as a software requirement for re-entrance and PSYC's decentralized variable state. Frequently such a data structure is called a "context", also in that case it has nothing to do with PSYC's notion of contexts.

API design rules

  1. Easy to learn.
  2. Hard to use it wrong.
  3. Easy to use, even without documentation.

On the other side, this is the most lowlevel API that will exist, so it is to be expected that only experienced users will use it to create bindings for other languages, even a more abstract one for C itself.

API design gotchas

For the renderer we define an elaborate struct to contain pointers to all pieces of data contained in the packet to be written. This is because we want to add up the string lengths of all items so that we can render the packet in one go with the length of the content precalculated (especially relevant when we want to leave it out!), thus avoiding any memory move operations and allowing for multiple renderings to take place in the same buffer. The other strategy we considered was to move the routing header in memory to fit the length info, but then the beginning of the packet isn't at the beginning of the buffer anymore, which makes it unnecessarily cumbersome to collect multiple packets into a single socket write() operation. We try however to hide the creation of the struct into (inline) functions so the API calls still look somewhat similar to the parsing ones.

The parser instead doesn't know the number of incoming variable modifications before parsing them, therefore it is a bit impractical to create a packet struct as parsing happens. We chose to do so anyhow, as the struct freezes the state changes before applying them. So we fulfil fippo's rule: If a packet generates an error before completion, all state changes are consistently rejected and can be applied safely when the packet is resent.

API documentation

API documentation for the latest stable version.

Benchmarks

libpsyc contains a benchmark document which describes a series of test packets in XML, JSON and PSYC formats and how well popular parser implementations manage to handle them. libpsyc and the PSYC syntax outperforms the other formats by at least the factor of 4 if not a lot more. See the document for details.

See also

  • Gotchas about C.