read_2004_section_handles (Bit_Chain *restrict dat, Dwg_Data *restrict dwg)
{
  Bit_Chain obj_dat = { NULL }, hdl_dat = { NULL };
  BITCODE_RS section_size = 0;
  long unsigned int endpos;
  int error;

  obj_dat.opts = hdl_dat.opts = dwg->opts & DWG_OPTS_LOGLEVEL;
  error = read_2004_compressed_section (dat, dwg, &obj_dat, SECTION_OBJECTS);
  if (error >= DWG_ERR_CRITICAL || !obj_dat.chain)
    {
      LOG_ERROR ("Failed to read compressed %s section", "AcDbObjects");
      if (obj_dat.chain)
        free (obj_dat.chain);
      return error;
    }

  error = read_2004_compressed_section (dat, dwg, &hdl_dat, SECTION_HANDLES);
  if (error >= DWG_ERR_CRITICAL || !hdl_dat.chain)
    {
      LOG_ERROR ("Failed to read compressed %s section", "Handles");
      free (obj_dat.chain);
      if (hdl_dat.chain)
        free (hdl_dat.chain);
      return error;
    }

  endpos = hdl_dat.byte + hdl_dat.size;
  dwg->num_objects = 0;

  do
    {
      long unsigned int last_offset;
      // long unsigned int last_handle;
      long unsigned int oldpos = 0;
      long unsigned int startpos = hdl_dat.byte;
      BITCODE_BL max_handles = hdl_dat.size * 2;
      uint16_t crc1, crc2;

      section_size = bit_read_RS_LE (&hdl_dat);
      LOG_TRACE ("\nHandles page size: %u [RS_LE]\n", section_size);
      /* ***********************************************
       * ODA p. 251 "Note that each section is cut off at a maximum length of
       * 2032." BUT in fact files exist with 2036 section size */
      if (section_size > 2040)
        {
          LOG_ERROR ("Object-map/handles page size greater than 2040!");
          free (hdl_dat.chain);
          free (obj_dat.chain);
          return DWG_ERR_VALUEOUTOFBOUNDS;
        }

      last_offset = 0;
      while ((long)(hdl_dat.byte - startpos) < (long)section_size)
        {
          int added;
          BITCODE_UMC handleoff;
          BITCODE_MC offset;
          BITCODE_BL last_handle = dwg->num_objects
            ? dwg->object[dwg->num_objects - 1].handle.value : 0;

          oldpos = hdl_dat.byte;
          // the offset from the previous handle. default: 1, unsigned
          handleoff = bit_read_UMC (&hdl_dat);
          // the offset from the previous address. default: obj->size
          offset = bit_read_MC (&hdl_dat);
          if (!handleoff || handleoff > max_handles - last_handle)
            {
              BITCODE_MC prevsize = dwg->num_objects
                ? dwg->object[dwg->num_objects - 1].size + 4 : 0;
              LOG_WARN ("Ignore invalid handleoff (@%lu)", oldpos)
              if (offset == 1 ||
                  (offset > 0 && offset < prevsize && prevsize > 0) ||
                  (offset < 0 && labs((long)offset) < prevsize && prevsize > 0))
                {
                  if (offset != prevsize)
                    LOG_ERROR ("Invalid offset: %ld [MC]", offset);
                  offset = prevsize;
                  LOG_WARN ("Recover invalid offset to %ld", offset);
                }
            }
          last_offset += offset;
          LOG_TRACE ("\n< Next object: %lu ", (unsigned long)dwg->num_objects)
          LOG_HANDLE ("Handleoff: %lX [UMC] "
                      "Offset: " FORMAT_MC " [MC] @%lu\n",
                      handleoff, offset, last_offset);

          if (hdl_dat.byte == oldpos) // ?? completely unrelated
            break;

          added = dwg_decode_add_object (dwg, &obj_dat, &obj_dat, last_offset);
          if (added > 0)
            error |= added;
          // else re-allocated
          // we don't stop encoding on single errors, but we sum them all up
          // as combined bitmask
        }

      if (hdl_dat.byte == oldpos)
        break;
#if 0
      if (!bit_check_CRC(&hdl_dat, startpos, 0xC0C1))
        error |= DWG_ERR_WRONGCRC;
      //LOG_WARN("Handles section CRC mismatch at offset %lx", startpos);
#else
      crc1 = bit_calc_CRC (0xC0C1, &(hdl_dat.chain[startpos]),
                           hdl_dat.byte - startpos);
      crc2 = bit_read_RS_LE (&hdl_dat);
      LOG_TRACE ("Handles page crc: %04X [RS_LE]\n", crc2);
      if (crc1 == crc2)
        {
          LOG_INSANE ("Handles page CRC: %04X from %lu-%lu=%ld\n", crc2,
                      startpos, hdl_dat.byte - 2, hdl_dat.byte - startpos - 2);
        }
      else
        {
          LOG_WARN (
              "Handles page CRC: %04X vs calc. %04X from %lu-%lu=%ld\n",
              crc2, crc1, startpos, hdl_dat.byte - 2, hdl_dat.byte - startpos - 2);
          error |= DWG_ERR_WRONGCRC;
        }
#endif

      if (hdl_dat.byte >= endpos)
        break;
    }
  while (section_size > 2);

  free (hdl_dat.chain);
  free (obj_dat.chain);
  return error;
}