Le’Blog - Le’Sec - Starting over

After a lot of thinking, I’ve decided to start over the Le’Sec project.

It wasn’t just about the details I was struggling with, but also about the way I had written the templates / C type factories. It’s a mess, there’s no other way to put it, which made it very hard (almost impossible) to debug. I also found myself annoyed by all the pointers that the application ended up juggling for an implementation.

Furthermore, I also realised that the core part of Le’Sec is more general than just for security purposes.

So I started to think about reimplementing these core parts, using other methods to get C type factories.

I’ve also been thinking about heap use and initialization, and taken some inspiration from Rust and Zig, which routinely copy small structs all over the place (at least conceptually… in practice, they may actually pass them by reference, but strictly const, so it really comes down to the same thing). This leads to a separation between allocation and initialization.

So I’ve been at it since the end of March, and for most of it, the new ideas are holding up.

  1. I reduced the base struct back to only two fields:

    typedef struct LeCore_st LeCore_t;
    typedef LE_STATUS LeCore_dispatch_fn(const LeCore_t le_subject,
                                         const unsigned int le_num, ...);
    struct LeCore_st {
      LeCore_dispatch_fn *le_dispatch;
      void *le_data;
    };
    

    It turned out, though, that the idea from Le’Sec core, of adding dispatch data for the benefit of the implementation still has benefits, so I expanded the above definition a bit:

    typedef struct LeCore_st LeCore_t;
    typedef struct LeCore_implementation_st LeCore_implementation_t;
    typedef LE_STATUS LeCore_dispatch_fn(const LeCore_t le_subject,
                                         const unsigned int le_num, ...);
    struct LeCore_st {
      /// The implementation data, as given by plugin implementations
      const LeCore_implementation_t *le_impl;
      /// The instance data
      void *le_data;
    };
    
    struct LeCore_implementation_st {
      /// The dispatch function.  This must never be NULL.
      LeCore_dispatch_fn *le_dispatch;
      /// The dispatch data.  This may be NULL.
      const void *le_dispatch_data;
    };
    

    With this, the plugin still only has to pass back one pointer for the application to juggle with.

  2. C type factories are now implemented through .h.inc files, which expect diverse symbols to be aliased to other names (so LeCore_t will never exist in reality, but will rather be an alias for, say, LeSec_Key_t).

    Alongside those, it becomes fairly easy to implement diverse bread&butter helper functions that can be called in a simple manner from a dispatch function. These are implemented in a corresponding .c.inc file, which function in a similar manner, with aliased symbols.

  3. Initialization of an implementation, and especially its private data, is now much more explicit, and as said earlier, separated from the allocation of the core structure. There is a specific dispatch commands for this, “construct”, and along with it, “destruct”.

These are the major parts that makes this reimplementation different from Le’Sec Core.