/*
 * alignclass.{cc,hh} -- element classes that know about alignment constraints
 * Eddie Kohler
 *
 * Copyright (c) 1999-2000 Massachusetts Institute of Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>

#include "alignclass.hh"
#include <click/confparse.hh>
#include <click/error.hh>
#include "routert.hh"
#include <string.h>

Alignment
common_alignment(const Vector<Alignment> &a, int off, int n)
{
  if (n == 0)
    return Alignment(1, 0);
  Alignment m = a[off];
  for (int i = 1; i < n; i++)
    m |= a[off+i];
  return m;
}

Alignment
combine_alignment(const Vector<Alignment> &a, int off, int n)
{
  if (n == 0)
    return Alignment(1, 0);
  Alignment m = a[off];
  for (int i = 1; i < n; i++)
    m &= a[off+i];
  return m;
}

void
Aligner::have_flow(const Vector<Alignment> &ain, int offin, int nin, Vector<Alignment> &aout, int offout, int nout)
{
  Alignment a = common_alignment(ain, offin, nin);
  for (int j = 0; j < nout; j++)
    aout[offout + j] = a;
}

void
Aligner::want_flow(Vector<Alignment> &ain, int offin, int nin, const Vector<Alignment> &aout, int offout, int nout)
{
  Alignment a = combine_alignment(aout, offout, nout);
  for (int j = 0; j < nin; j++)
    ain[offin + j] = a;
}

void
Aligner::adjust_flow(Vector<Alignment> &, int, int, const Vector<Alignment> &, int, int)
{
}

void
NullAligner::have_flow(const Vector<Alignment> &, int, int, Vector<Alignment> &, int, int)
{
}

void
NullAligner::want_flow(Vector<Alignment> &, int, int, const Vector<Alignment> &, int, int)
{
}

void
CombinedAligner::have_flow(const Vector<Alignment> &ain, int offin, int nin, Vector<Alignment> &aout, int offout, int nout)
{
  _have->have_flow(ain, offin, nin, aout, offout, nout);
}

void
CombinedAligner::want_flow(Vector<Alignment> &ain, int offin, int nin, const Vector<Alignment> &aout, int offout, int nout)
{
  _want->want_flow(ain, offin, nin, aout, offout, nout);
}

void
GeneratorAligner::have_flow(const Vector<Alignment> &, int, int, Vector<Alignment> &aout, int offout, int nout)
{
  for (int j = 0; j < nout; j++)
    aout[offout + j] = _alignment;
}

void
GeneratorAligner::want_flow(Vector<Alignment> &ain, int offin, int nin, const Vector<Alignment> &, int, int)
{
  for (int j = 0; j < nin; j++)
    ain[offin + j] = Alignment();
}

void
ShifterAligner::have_flow(const Vector<Alignment> &ain, int offin, int nin, Vector<Alignment> &aout, int offout, int nout)
{
  Alignment a = common_alignment(ain, offin, nin);
  a += _shift;
  for (int j = 0; j < nout; j++)
    aout[offout + j] = a;
}

void
ShifterAligner::want_flow(Vector<Alignment> &ain, int offin, int nin, const Vector<Alignment> &aout, int offout, int nout)
{
  Alignment a = combine_alignment(aout, offout, nout);
  a -= _shift;
  for (int j = 0; j < nin; j++)
    ain[offin + j] = a;
}

void
WantAligner::want_flow(Vector<Alignment> &ain, int offin, int nin, const Vector<Alignment> &, int, int)
{
  for (int j = 0; j < nin; j++)
    ain[offin + j] = _alignment;
}

void
ClassifierAligner::adjust_flow(Vector<Alignment> &ain, int offin, int nin, const Vector<Alignment> &, int, int)
{ 
  Alignment a = common_alignment(ain, offin, nin);
  if (a.chunk() < 4)
    a = Alignment(4, a.offset());
  for (int j = 0; j < nin; j++)
    ain[offin + j] = a;
}


Aligner *
default_aligner()
{
  static Aligner *a;
  if (!a) a = new Aligner;
  return a;
}


AlignClass::AlignClass(const String &name)
  : ElementClassT(name), _aligner(default_aligner())
{
}

AlignClass::AlignClass(const String &name, Aligner *a)
  : ElementClassT(name), _aligner(a)
{
}

Aligner *
AlignClass::create_aligner(ElementT *, RouterT *, ErrorHandler *)
{
  return _aligner;
}

void *
AlignClass::cast(const char *s)
{
  if (strcmp(s, "AlignClass") == 0)
    return (void *)this;
  else
    return 0;
}


StripAlignClass::StripAlignClass()
  : AlignClass("Strip")
{
}

Aligner *
StripAlignClass::create_aligner(ElementT *e, RouterT *, ErrorHandler *errh)
{
  int m;
  ContextErrorHandler cerrh(errh, "While analyzing alignment for '" + e->declaration() + "':");
  if (cp_va_parse(e->configuration(), &cerrh,
		  cpInteger, "amount to strip", &m,
		  cpEnd) < 0)
    return default_aligner();
  return new ShifterAligner(m);
}


CheckIPHeaderAlignClass::CheckIPHeaderAlignClass(const String &name, int argno)
  : AlignClass(name), _argno(argno)
{
}

Aligner *
CheckIPHeaderAlignClass::create_aligner(ElementT *e, RouterT *, ErrorHandler *errh)
{
  unsigned offset = 0;
  Vector<String> args;
  cp_argvec(e->configuration(), args);
  if (args.size() > _argno) {
    if (!cp_unsigned(args[_argno], &offset)) {
      ContextErrorHandler cerrh(errh, "While analyzing alignment for '" + e->declaration() + "':");
      cerrh.error("argument %d should be IP header offset (unsigned)", _argno + 1);
      return default_aligner();
    }
  }
  return new WantAligner(Alignment(4, 0) - (int)offset);
}


AlignAlignClass::AlignAlignClass()
  : AlignClass("Align")
{
}

Aligner *
AlignAlignClass::create_aligner(ElementT *e, RouterT *, ErrorHandler *errh)
{
  int offset, chunk;
  ContextErrorHandler cerrh(errh, "While analyzing alignment for '" + e->declaration() + "':");
  if (cp_va_parse(e->configuration(), &cerrh,
		  cpUnsigned, "alignment modulus", &chunk,
		  cpUnsigned, "alignment offset", &offset,
		  cpEnd) < 0)
    return default_aligner();
  return new GeneratorAligner(Alignment(chunk, offset));
}


syntax highlighted by Code2HTML, v. 0.9.1