<'
#
# Copyright (c) 2012-2014, 2017, 2020-2021, 2026 LAAS/CNRS
# All rights reserved.
#
# Redistribution  and  use  in  source  and binary  forms,  with  or  without
# modification, are permitted provided that the following conditions are met:
#
#   1. Redistributions of  source  code must retain the  above copyright
#      notice and this list of conditions.
#   2. Redistributions in binary form must reproduce the above copyright
#      notice and  this list of  conditions in the  documentation and/or
#      other materials provided with the distribution.
#
# THE SOFTWARE  IS PROVIDED "AS IS"  AND THE AUTHOR  DISCLAIMS ALL WARRANTIES
# WITH  REGARD   TO  THIS  SOFTWARE  INCLUDING  ALL   IMPLIED  WARRANTIES  OF
# MERCHANTABILITY AND  FITNESS.  IN NO EVENT  SHALL THE AUTHOR  BE LIABLE FOR
# ANY  SPECIAL, DIRECT,  INDIRECT, OR  CONSEQUENTIAL DAMAGES  OR  ANY DAMAGES
# WHATSOEVER  RESULTING FROM  LOSS OF  USE, DATA  OR PROFITS,  WHETHER  IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR  OTHER TORTIOUS ACTION, ARISING OUT OF OR
# IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
#                                           Anthony Mallet on Wed Feb 29 2012
#

args types
lang c

# expand types, uniquifying on mangled name
set tdict [dict create]
foreach t $types {
  foreach e [$t types] {
    dict set tdict [$e mangle] $e
  }
}
'>
#ifndef H_GENOM_TYPEOPS_C
#define H_GENOM_TYPEOPS_C

#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

<"[language mapping [dict values $tdict] off]">

<'dict for {m t} $tdict {'>

static inline void	genom_tinit_<"$m">(
				<"[$t argument reference]">);
static inline void	genom_tfini_<"$m">(
				<"[$t argument reference]">);
static inline int	genom_tcopy_<"$m">(
				<"[$t argument reference]">,
				<"[$t argument value]">);
<'}'>

static inline void	genom_tinitex(genom_event ex, void *exdetail);
static inline void	genom_tfiniex(genom_event ex, void *exdetail);
static inline int	genom_tcopyex(genom_event ex, void *ddst,
                                const void *dsrc);

<'dict for {m t} $tdict {'>

/* <"[--- [$t mangle] ===================================================]"> */

static inline void
genom_tinit_<"$m">(<"[$t argument reference data]">)
{
<'  switch -glob -- [$t kind] {'>
<'    native {'>
  *data = NULL;
<'    }'>
<'    typedef - {* member} {'>
  genom_tinit_<"[[$t type] mangle]">(data);
<'    }'>
<'    sequence {'>
<'      if {[catch {$t length} l]} {'>
  data->_maximum = data->_length = 0;
  data->_buffer = NULL;
  data->_release = NULL;
<'      } else {'>
  uint32_t i;
  data->_maximum = <"$l">;
  data->_length = 0;
  for (i=0; i<<"$l">; i++)
    genom_tinit_<"[[$t type] mangle]">(
      <"[[$t type] pass reference {data->_buffer[i]}]">);
<'      }'>
<'    }'>
<'    string {'>
<'      if {[catch {$t length}]} {'>
  *data = NULL;
<'      } else {'>
  *data = '\0';
<'      }'>
<'    }'>
<'    struct - exception {'>
<'      if {![llength [$t members]]} {'>
  (void)data; /* -Wunused-parameter */
<'      }'>
<'      foreach e [$t members] {'>
  genom_tinit_<"[$e mangle]">(<"[$e pass reference data->[$e name]]">);
<'      }'>
<'    }'>
<'    union {'>
<'      foreach first [$t members] {'>
<'        foreach v [$first value] { if {$v != ""} break }'>
<'        if {$v != ""} break'>
<'      }'>
  genom_tinit_<"[[$t disc] mangle]">(<"[[$t disc] pass reference data->_d]">);
<'      if {$v != ""} {'>
  data->_d = <"[language::cname $v]">;
<'      }'>
  genom_tinit_<"[$first mangle]">(
    <"[$first pass reference data->_u.[$first name]]">);
<'    }'>
<'    optional {'>
  genom_tinit_<"[[$t type] mangle]">(<"[[$t type] pass reference data->_value]">);
<'    }'>
<'    array {'>
  uint32_t i;
  for (i=0; i<<"[$t length]">; i++)
    genom_tinit_<"[[$t type] mangle]">(
      <"[[$t type] pass reference {data[i]}]">);
<'    }'>
<'    default {'>
  (void)data; /* -Wunused-parameter */
<'    }'>
<'  }'>
}

static inline void
genom_tfini_<"$m">(<"[$t argument reference data]">)
{
<'  switch -glob -- [$t kind] {'>
<'    typedef - {* member} {'>
  genom_tfini_<"[[$t type] mangle]">(data);
<'    }'>
<'    sequence {'>
  uint32_t i;
<'      if {[catch {$t length} l]} {'>
  for (i=0; i<data->_length; i++)
    genom_tfini_<"[[$t type] mangle]">(
      <"[[$t type] pass reference {data->_buffer[i]}]">);
  data->_maximum = data->_length = 0;
  if (data->_release && data->_buffer) data->_release(data->_buffer);
  data->_buffer = NULL;
  data->_release = NULL;
<'      } else {'>
  for (i=0; i<<"$l">; i++)
    genom_tfini_<"[[$t type] mangle]">(
      <"[[$t type] pass reference {data->_buffer[i]}]">);
<'      }'>
<'    }'>
<'    string {'>
<'      if {[catch {$t length}]} {'>
  if (*data) {
    free(*data);
    *data = NULL;
  }
<'      }'>
<'    }'>
<'    struct - exception {'>
<'      if {![llength [$t members]]} {'>
  (void)data; /* -Wunused-parameter */
<'      }'>
<'      foreach e [$t members] {'>
  genom_tfini_<"[$e mangle]">(<"[$e pass reference data->[$e name]]">);
<'      }'>
<'    }'>
<'    union {'>
  switch(data->_d) {
<'      foreach e [$t members] {'>
<'        foreach v [$e value] {'>
<'          if {$v != ""} {'>
    case <"[language::cname $v]">:
<'          } else {'>
    default:
<'          }'>
<'        }'>
      genom_tfini_<"[$e mangle]">(<"[$e pass reference data->_u.[$e name]]">);
      break;
<'      }'>
  }
  genom_tfini_<"[[$t disc] mangle]">(<"[[$t disc] pass reference data->_d]">);
<'    }'>
<'    optional {'>
  genom_tfini_<"[[$t type] mangle]">(<"[[$t type] pass reference data->_value]">);
<'    }'>
<'    array {'>
  uint32_t i;
  for (i=0; i<<"[$t length]">; i++)
    genom_tfini_<"[[$t type] mangle]">(
      <"[[$t type] pass reference {data[i]}]">);
<'    }'>
<'    default {'>
  (void)data; /* -Wunused-parameter */
<'    }'>
<'  }'>
}

static inline int
genom_tcopy_<"$m">(<"[$t argument reference dst]">,
  <"[$t argument value src]">)
{
<'  set fixed [expr {[$t fixed]?"fixed":"variable"}]'>
<'  switch -glob -- $fixed+[$t kind] {'>
<'    *+native {'>
  *dst = (<"[$t declarator]">)src;
  return 0;
<'    }'>
<'    *+typedef - {*+* member} {'>
  return genom_tcopy_<"[[$t type] mangle]">(dst, src);
<'    }'>
<'    fixed+sequence {'>
  assert(src->_length <= <"[$t length]">);
  dst->_length = src->_length;
  memcpy(dst->_buffer, src->_buffer, src->_length * sizeof(*src->_buffer));
  return 0;
<'    }'>
<'    fixed+array {'>
  memcpy(dst, src, <"[$t length]"> * sizeof(*src));
  return 0;
<'    }'>
<'    fixed+string {'>
  strncpy(dst, src, <"[$t length]">);
  return 0;
<'    }'>
<'    fixed+struct - fixed+union - fixed+exception {'>
<'      if {[llength [$t members]]} {'>
  memcpy(dst, src, sizeof(<"[$t declarator]">));
<'      } else {'>
  (void)dst; (void)src; /* fix -Wunused-parameter */
<'      }'>
  return 0;
<'    }'>
<'    *+optional {'>
  dst->_present = src->_present;
  if (src->_present)
    return genom_tcopy_<"[[$t type] mangle]">(
      <"[[$t type] pass reference dst->_value]">,
      <"[[$t type] pass value src->_value]">);
  return 0;
<'    }'>
<'    fixed+port {'>
  memcpy(dst, src, sizeof(<"[$t declarator]">));
  return 0;
<'    }'>
<'    fixed+* {'>
  <"[$t dereference reference dst]"> = <"[$t dereference value src]">;
  return 0;
<'    }'>
<'    variable+struct - variable+exception {'>
<'      if {[llength [$t members]]} {'>
  int s;
<'        foreach e [$t members] {'>
  s = genom_tcopy_<"[$e mangle]">(
    <"[$e pass reference dst->[$e name]]">,
    <"[$e pass value src->[$e name]]">);
  if (s) return s;
<'        }'>
<'      } else {'>
  (void)dst; (void)src; /* fix -Wunused-parameter */
<'      }'>
  return 0;
<'    }'>
<'    variable+union {'>
  int s;
  switch(data->_d) {
<'      foreach e [$t members] {'>
<'        foreach v [$e value] {'>
<'          if {$v != ""} {'>
    case <"[language::cname $v]">:
<'          } else {'>
    default:
<'          }'>
<'        }'>
      s = genom_tcopy_<"[$e mangle]">(
        <"[$e pass reference dst->_u.[$e name]]">,
        <"[$e pass value src->_u.[$e name]]">);
      if (s) return s;
      break;
<'      }'>
  }
  s = genom_tcopy_<"[[$t disc] mangle]">(
    <"[[$t disc] pass reference dst->_d]">,
    <"[[$t disc] pass value src->_d]">);
  return s;
<'    }'>
<'    variable+sequence {'>
  int s;
  uint32_t i;
<'      if {[catch {$t length}]} {'>
  if (genom_sequence_reserve(dst, src->_maximum)) return ENOMEM;
<'      }'>
  dst->_length = src->_length;
  for (i=0; i<src->_length; i++) {
    s = genom_tcopy_<"[[$t type] mangle]">(
      <"[[$t type] pass reference {dst->_buffer[i]}]">,
      <"[[$t type] pass value {src->_buffer[i]}]">);
    if (s) return s;
  }
  return 0;
<'    }'>
<'    variable+array {'>
  int s;
  uint32_t i;
  for (i=0; i<<"[$t length]">; i++) {
    s = genom_tcopy_<"[[$t type] mangle]">(
      <"[[$t type] pass reference {dst[i]}]">,
      <"[[$t type] pass value {src[i]}]">);
    if (s) return s;
  }
  return 0;
<'    }'>
<'    variable+string {'>
  if (*dst) free(*dst);
  *dst = src ? strdup(src) : NULL;
  return src && !*dst ? ENOMEM : 0;
<'    }'>
<'    default { error "reached unreachable" } '>
<'  }'>
}

<'}'>
/* === Exceptions ========================================================== */

static inline void
genom_tinitex(genom_event ex, void *exdetail)
{
  if (0) return;
<'dict for {m t} $tdict {'>
<'  if {[$t kind] != "exception"} continue'>
<'  if {![llength [$t members]]} continue'>
  else if (ex == <"[$t cname]">_id)
    genom_tinit_<"$m">((<"[$t declarator *]">)exdetail);
<'}'>
}

static inline void
genom_tfiniex(genom_event ex, void *exdetail)
{
  if (0) return;
<'dict for {m t} $tdict {'>
<'  if {[$t kind] != "exception"} continue'>
<'  if {![llength [$t members]]} continue'>
  else if (ex == <"[$t cname]">_id)
    genom_tfini_<"$m">((<"[$t declarator *]">)exdetail);
<'}'>
}

static inline int
genom_tcopyex(genom_event ex, void *ddst, const void *dsrc)
{
  if (ex == genom_syserr_id) {
    memcpy(ddst, dsrc, sizeof(genom_syserr_detail));
    return 0;
  } else if (ex == genom_unkex_id) {
    memcpy(ddst, dsrc, sizeof(genom_unkex_detail));
    return 0;
  }
<'dict for {m t} $tdict {'>
<'  if {[$t kind] != "exception"} continue'>
<'  if {![llength [$t members]]} continue'>
  else if (ex == <"[$t cname]">_id)
    return genom_tcopy_<"$m">(
      (<"[$t declarator *]">)ddst,
      (<"[$t declarator * const]">)dsrc);
<'}'>
  return 0;
}

#endif /* H_GENOM_TYPEOPS_C */
