The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*******************************************************************************
*
* MODULE: bitfields.c
*
********************************************************************************
*
* DESCRIPTION: Bitfield layouting routines
*
********************************************************************************
*
* Copyright (c) 2002-2015 Marcus Holland-Moritz. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the same terms as Perl itself.
*
*******************************************************************************/

/*===== GLOBAL INCLUDES ======================================================*/

#include <assert.h>
#include <string.h>
#include <stddef.h>


/*===== LOCAL INCLUDES =======================================================*/

#include "ctlib/ctdebug.h"
#include "ctlib/cterror.h"
#include "ctlib/bitfields.h"

#include "util/ccattr.h"
#include "util/memalloc.h"


/*===== DEFINES ==============================================================*/

#define REG_BL_CLASS(cls)  { #cls, sizeof(struct _BL_ ## cls), &cls ## _vtable }
#define BL_SELF(cls)       BL_ ## cls self = (BL_ ## cls) _self
#define aSELF              BitfieldLayouter _self __attribute__((unused))

#ifdef BITS
#  undef BITS
#endif
#define BITS(bytes)        (8*(bytes))

#define BYTE_ORDER_STRING  (self->byte_order == BLPV_BIG_ENDIAN ? "BE" : "LE")

/*===== TYPEDEFS =============================================================*/

typedef struct _BL_Generic {
  BL_CLASS_FIXED;
  BLPropValStr byte_order;
  BLPropValInt max_align;
  BLPropValInt align;
  BLPropValInt offset;
  int bit_offset;
  int cur_type_size;
  int cur_type_align;
} *BL_Generic;

typedef struct _BL_Microsoft {
  BL_CLASS_FIXED;
  BLPropValStr byte_order;
  BLPropValInt max_align;
  BLPropValInt align;
  BLPropValInt offset;
  int bit_offset;
  int cur_type_size;
  int cur_type_align;
} *BL_Microsoft;

typedef struct _BL_Simple {
  BL_CLASS_FIXED;
  BLPropValStr byte_order;
  BLPropValInt max_align;
  BLPropValInt align;
  BLPropValInt offset;
  BLPropValInt block_size;
  int pos;
  int bits_left;
} *BL_Simple;


/*===== STATIC FUNCTION PROTOTYPES ===========================================*/

static void             Generic_init(aSELF);
static void             Generic_reset(aSELF);
static enum BLError     Generic_push(aSELF, const BLPushParam *pParam);
static enum BLError     Generic_finalize(aSELF);
static enum BLError     Generic_get(aSELF, BLProperty prop, BLPropValue *value);
static enum BLError     Generic_set(aSELF, BLProperty prop, const BLPropValue *value);
static const BLOption * Generic_options(aSELF, int *count);

static void             Microsoft_init(aSELF);
static void             Microsoft_reset(aSELF);
static enum BLError     Microsoft_push(aSELF, const BLPushParam *pParam);
static enum BLError     Microsoft_finalize(aSELF);
static enum BLError     Microsoft_get(aSELF, BLProperty prop, BLPropValue *value);
static enum BLError     Microsoft_set(aSELF, BLProperty prop, const BLPropValue *value);
static const BLOption * Microsoft_options(aSELF, int *count);

static void             Simple_init(aSELF);
static void             Simple_reset(aSELF);
static enum BLError     Simple_push(aSELF, const BLPushParam *pParam);
static enum BLError     Simple_finalize(aSELF);
static enum BLError     Simple_get(aSELF, BLProperty prop, BLPropValue *value);
static enum BLError     Simple_set(aSELF, BLProperty prop, const BLPropValue *value);
static const BLOption * Simple_options(aSELF, int *count);

static void             bl_destroy(BitfieldLayouter self);
static BitfieldLayouter bl_clone(BitfieldLayouter self);
static const char *     bl_class_name(BitfieldLayouter self);


/*===== EXTERNAL VARIABLES ===================================================*/

/*===== GLOBAL VARIABLES =====================================================*/

/*===== STATIC VARIABLES =====================================================*/

static const struct BLVtable Generic_vtable = {
  bl_clone,
  Generic_init,
  Generic_reset,
  bl_destroy,
  Generic_push,
  Generic_finalize,
  Generic_get,
  Generic_set,
  Generic_options,
  bl_class_name,
};

static const struct BLVtable Microsoft_vtable = {
  bl_clone,
  Microsoft_init,
  Microsoft_reset,
  bl_destroy,
  Microsoft_push,
  Microsoft_finalize,
  Microsoft_get,
  Microsoft_set,
  Microsoft_options,
  bl_class_name,
};

static const struct BLVtable Simple_vtable = {
  bl_clone,
  Simple_init,
  Simple_reset,
  bl_destroy,
  Simple_push,
  Simple_finalize,
  Simple_get,
  Simple_set,
  Simple_options,
  bl_class_name,
};

static const struct BLClass {
  const char *name;
  const size_t size;
  const struct BLVtable *vtbl;
} bl_classes[] = {
  REG_BL_CLASS(Generic),
  REG_BL_CLASS(Microsoft),
  REG_BL_CLASS(Simple)
};


/*===== STATIC FUNCTIONS =====================================================*/

/*******************************************************************************
********************************************************************************
********************************************************************************
                       ______                     _     
                      / ____/__  ____  ___  _____(_)____
                     / / __/ _ \/ __ \/ _ \/ ___/ / ___/
                    / /_/ /  __/ / / /  __/ /  / / /__  
                    \____/\___/_/ /_/\___/_/  /_/\___/  

********************************************************************************
********************************************************************************
*******************************************************************************/

/*******************************************************************************
*
*   ROUTINE: Generic_init
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void Generic_init(aSELF)
{
  BL_SELF(Generic);

  self->byte_order = BLPV_LITTLE_ENDIAN;
}

/*******************************************************************************
*
*   ROUTINE: Generic_reset
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void Generic_reset(aSELF)
{
  BL_SELF(Generic);

  self->bit_offset     = 0;
  self->cur_type_size  = 0;
  self->cur_type_align = 0;
}

/*******************************************************************************
*
*   ROUTINE: Generic_push
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static enum BLError Generic_push(aSELF, const BLPushParam *pParam)
{
  BL_SELF(Generic);
  BitfieldInfo *bit;

  assert(pParam->pDecl != NULL);
  assert(pParam->pDecl->bitfield_flag);

  bit = &pParam->pDecl->ext.bitfield;

  CT_DEBUG(CTLIB, ("(Generic) pushing bitfield (%s:%d/s=%d/a=%d), offset=%d.%d, max_align=%d",
                   pParam->pDecl->identifier, bit->bits,
                   pParam->type_size, pParam->type_align,
                   (int) self->offset, self->bit_offset, (int) self->max_align));

  if (self->cur_type_size != (int) pParam->type_size)
  {
    int align = (int) pParam->type_align < self->max_align
              ? (int) pParam->type_align : self->max_align;
    int delta = self->offset % align;

    if (align > self->align)
      self->align = align;

    self->offset     -= delta;
    self->bit_offset += BITS(delta);

    CT_DEBUG(CTLIB, ("(Generic) type size change: size: %d -> %d, align: %d -> %d, offset=%d.%d",
                     self->cur_type_size, pParam->type_size, self->cur_type_align, align,
                     (int) self->offset, self->bit_offset));
  
    self->cur_type_size  = pParam->type_size;
    self->cur_type_align = align;
  }

  while (bit->bits > BITS(self->cur_type_size) - self->bit_offset)
  {
    self->offset += self->cur_type_align;

    if (self->bit_offset > BITS(self->cur_type_align))
      self->bit_offset -= BITS(self->cur_type_align);
    else
      self->bit_offset = 0;

    CT_DEBUG(CTLIB, ("(Generic) move offset -> %d.%d",
                     (int) self->offset, self->bit_offset));
  }

  if (bit->bits == 0)
  {
    if (self->bit_offset > 0)
    {
      self->offset    += self->cur_type_size - (self->offset % self->cur_type_size);
      self->bit_offset = 0;
    }
  }
  else
  {
    int used_bytes, new_bit_offset;

    new_bit_offset = self->bit_offset + bit->bits;

    if (new_bit_offset <= BITS(1))
      used_bytes = 1;
    else if (new_bit_offset <= BITS(2))
      used_bytes = 2;
    else if (new_bit_offset <= BITS(4))
      used_bytes = 4;
    else if (new_bit_offset <= BITS(8))
      used_bytes = 8;

    assert(used_bytes <= self->cur_type_size);

    pParam->pDecl->offset = self->offset;
    pParam->pDecl->size   = used_bytes;

    bit->size             = used_bytes;

    switch (self->byte_order)
    {
      case BLPV_LITTLE_ENDIAN:
        bit->pos = self->bit_offset;
        break;

      case BLPV_BIG_ENDIAN:
        bit->pos = BITS(used_bytes) - self->bit_offset - bit->bits;
        break;

      default:
        fatal_error("(Generic) invalid byte-order (%d)", self->byte_order);
        break;
    }

    assert(bit->pos < 64);

    self->bit_offset = new_bit_offset;
  }

  CT_DEBUG(CTLIB, ("(Generic) new %s bitfield (%s) at (offset=%d, size=%d, pos=%d, bits=%d)",
                   BYTE_ORDER_STRING, pParam->pDecl->identifier,
                   pParam->pDecl->offset, bit->size, bit->pos, bit->bits));

  return BLE_NO_ERROR;
}

/*******************************************************************************
*
*   ROUTINE: Generic_finalize
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static enum BLError Generic_finalize(aSELF)
{
  BL_SELF(Generic);

  CT_DEBUG(CTLIB, ("(Generic) finalizing bitfield (offset=%d.%d)",
                   (int) self->offset, self->bit_offset));

  self->offset += (self->bit_offset + (BITS(1)-1)) / BITS(1);

  CT_DEBUG(CTLIB, ("(Generic) final offset=%d", (int) self->offset));

  return BLE_NO_ERROR;
}

/*******************************************************************************
********************************************************************************
********************************************************************************
                  __  ____                            ______ 
                 /  |/  (_)_____________  _________  / __/ /_
                / /|_/ / / ___/ ___/ __ \/ ___/ __ \/ /_/ __/
               / /  / / / /__/ /  / /_/ (__  ) /_/ / __/ /_  
              /_/  /_/_/\___/_/   \____/____/\____/_/  \__/  
                                              
********************************************************************************
********************************************************************************
*******************************************************************************/

/*******************************************************************************
*
*   ROUTINE: Microsoft_init
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void Microsoft_init(aSELF)
{
  BL_SELF(Microsoft);

  self->byte_order = BLPV_LITTLE_ENDIAN;
}

/*******************************************************************************
*
*   ROUTINE: Microsoft_reset
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void Microsoft_reset(aSELF)
{
  BL_SELF(Microsoft);

  self->bit_offset     = 0;
  self->cur_type_size  = 0;
  self->cur_type_align = 0;
}

/*******************************************************************************
*
*   ROUTINE: Microsoft_push
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static enum BLError Microsoft_push(aSELF, const BLPushParam *pParam)
{
  BL_SELF(Microsoft);
  BitfieldInfo *bit;

  assert(pParam->pDecl != NULL);
  assert(pParam->pDecl->bitfield_flag);

  bit = &pParam->pDecl->ext.bitfield;

  if (self->cur_type_size != (int) pParam->type_size)
  {
    int delta;
    int align = (int) pParam->type_align < self->max_align
              ? (int) pParam->type_align : self->max_align;

    if (align > self->align)
      self->align = align;

    if (self->bit_offset > 0)
    {
      self->offset    += self->cur_type_size;
      self->bit_offset = 0;
    }

    delta = self->offset % align;

    if (delta)
    {
      self->offset    += align - delta;
      self->bit_offset = 0;
    }

    self->cur_type_size  = pParam->type_size;
    self->cur_type_align = align;
  }

  if (bit->bits == 0)
  {
    if (self->bit_offset > 0)
    {
      self->offset    += self->cur_type_size;
      self->bit_offset = 0;
    }
  }
  else
  {
    if (bit->bits > BITS(self->cur_type_size) - self->bit_offset)
    {
      if (bit->bits > BITS(self->cur_type_size))
        return BLE_BITFIELD_TOO_WIDE;

      self->offset    += self->cur_type_size;
      self->bit_offset = 0;
    }

    switch (self->byte_order)
    {
      case BLPV_LITTLE_ENDIAN:
        bit->pos = self->bit_offset;
        break;

      case BLPV_BIG_ENDIAN:
        bit->pos = BITS(self->cur_type_size) - self->bit_offset - bit->bits;
        break;

      default:
        fatal_error("(Microsoft) invalid byte-order (%d)", self->byte_order);
        break;
    }

    assert(bit->pos < 64);

    self->bit_offset += bit->bits;

    pParam->pDecl->offset = self->offset;
    pParam->pDecl->size   = self->cur_type_size;
    bit->size             = self->cur_type_size;

    CT_DEBUG(CTLIB, ("(Microsoft) new %s bitfield (%s) at (offset=%d, size=%d, pos=%d, bits=%d), bit_offset=%d",
                     BYTE_ORDER_STRING, pParam->pDecl->identifier,
                     pParam->pDecl->offset, bit->size, bit->pos, bit->bits, self->bit_offset));
  }

  return BLE_NO_ERROR;
}

/*******************************************************************************
*
*   ROUTINE: Microsoft_finalize
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static enum BLError Microsoft_finalize(aSELF)
{
  BL_SELF(Microsoft);

  if (self->bit_offset > 0)
    self->offset += self->cur_type_size;

  return BLE_NO_ERROR;
}

/*******************************************************************************
********************************************************************************
********************************************************************************
                        _____ _                 __   
                       / ___/(_)___ ___  ____  / /__ 
                       \__ \/ / __ `__ \/ __ \/ / _ \
                      ___/ / / / / / / / /_/ / /  __/
                     /____/_/_/ /_/ /_/ .___/_/\___/ 
                                     /_/             
********************************************************************************
********************************************************************************
*******************************************************************************/

/*******************************************************************************
*
*   ROUTINE: Simple_init
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void Simple_init(aSELF)
{
  BL_SELF(Simple);

  self->byte_order = BLPV_LITTLE_ENDIAN;
  self->block_size = 4;
}

/*******************************************************************************
*
*   ROUTINE: Simple_reset
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void Simple_reset(aSELF)
{
  BL_SELF(Simple);

  self->offset   += self->block_size - (self->offset % self->block_size);
  self->pos       = 0;
  self->bits_left = BITS(self->block_size);
}

/*******************************************************************************
*
*   ROUTINE: Simple_push
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static enum BLError Simple_push(aSELF, const BLPushParam *pParam)
{
  BL_SELF(Simple);
  BitfieldInfo *bit;

  assert(pParam->pDecl != NULL);
  assert(pParam->pDecl->bitfield_flag);

  bit = &pParam->pDecl->ext.bitfield;

  if (bit->bits == 0)
  {
    self->pos += self->block_size;
    self->bits_left = BITS(self->block_size);
  }
  else
  {
    if (bit->bits > self->bits_left)
    {
      self->pos += self->block_size;
      self->bits_left = BITS(self->block_size);
    }

    pParam->pDecl->offset = self->offset + self->pos;
    pParam->pDecl->size   = self->block_size;

    bit->size = (unsigned char) self->block_size;

    switch (self->byte_order)
    {
      case BLPV_LITTLE_ENDIAN:
        bit->pos = BITS(self->block_size) - self->bits_left;
        break;

      case BLPV_BIG_ENDIAN:
        bit->pos = self->bits_left - bit->bits;
        break;

      default:
        fatal_error("(Simple) invalid byte-order (%d)", self->byte_order);
        break;
    }

    self->bits_left -= bit->bits;

    CT_DEBUG(CTLIB, ("(Simple) new %s bitfield (%s) at (offset=%d, size=%d, pos=%d, bits=%d), bits_left=%d",
                     BYTE_ORDER_STRING, pParam->pDecl->identifier,
                     pParam->pDecl->offset, pParam->pDecl->size, bit->pos, bit->bits, self->bits_left));
  }

  return BLE_NO_ERROR;
}

/*******************************************************************************
*
*   ROUTINE: Simple_finalize
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static enum BLError Simple_finalize(aSELF)
{
  BL_SELF(Simple);

  if (self->bits_left != BITS(self->block_size))
    self->pos += self->block_size;

  self->offset += self->pos;
  self->align   = self->block_size;

  return BLE_NO_ERROR;
}

/*******************************************************************************
********************************************************************************
********************************************************************************
    __    _ __  _____      __    __     __                        __           
   / /_  (_) /_/ __(_)__  / /___/ /    / /___ ___  ______  __  __/ /____  _____
  / __ \/ / __/ /_/ / _ \/ / __  /    / / __ `/ / / / __ \/ / / / __/ _ \/ ___/
 / /_/ / / /_/ __/ /  __/ / /_/ /    / / /_/ / /_/ / /_/ / /_/ / /_/  __/ /    
/_.___/_/\__/_/ /_/\___/_/\__,_/____/_/\__,_/\__, /\____/\__,_/\__/\___/_/     
                              /_____/       /____/                             
********************************************************************************
********************************************************************************
*******************************************************************************/

/*******************************************************************************
*
*   ROUTINE: bl_destroy
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION: Destroys a BitfieldLayouter.
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static void bl_destroy(BitfieldLayouter self)
{
  if (self)
    Free(self);
}

/*******************************************************************************
*
*   ROUTINE: bl_clone
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION: Clones a BitfieldLayouter.
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static BitfieldLayouter bl_clone(BitfieldLayouter self)
{
  BitfieldLayouter clone;
  const struct BLClass *pc;

  assert(self != NULL);

  pc = self->blc;

  AllocF(BitfieldLayouter, clone, pc->size);
  memcpy(clone, self, pc->size);

  return clone;
}

/*******************************************************************************
*
*   ROUTINE: bl_class_name
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION: Returns the class name of a BitfieldLayouter.
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

static const char *bl_class_name(BitfieldLayouter self)
{
  assert(self != NULL);
  return self->blc->name;
}

/*******************************************************************************
*
*   ROUTINE: bl_create
*
*   WRITTEN BY: Marcus Holland-Moritz             ON: May 2005
*   CHANGED BY:                                   ON:
*
********************************************************************************
*
* DESCRIPTION:
*
*   ARGUMENTS:
*
*     RETURNS:
*
*******************************************************************************/

BitfieldLayouter bl_create(const char *class_name)
{
  BitfieldLayouter self;
  unsigned i;
  const struct BLClass *pc = NULL;

  assert(class_name != NULL);

  CT_DEBUG(CTLIB, ("trying to create new [%s] bitfield layouter", class_name));

  for (i = 0; i < sizeof bl_classes / sizeof bl_classes[0]; i++)
    if (strcmp(class_name, bl_classes[i].name) == 0)
    {
      pc = &bl_classes[i];
      break;
    }

  if (pc == NULL)
  {
    CT_DEBUG(CTLIB, ("no such bitfield layouter class [%s]", class_name));
    return NULL;
  }

  AllocF(BitfieldLayouter, self, pc->size);
  memset(self, 0, pc->size);

  self->blc = pc;
  self->m = pc->vtbl;

  if (self->m->init)
    self->m->init(self);

  CT_DEBUG(CTLIB, ("created new [%s] bitfield layouter", class_name));

  return self;
}

#include "token/t_blproperty.c"