/** @file
    WCCP Message parsing and generation.

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
 */

#include "WccpLocal.h"
#include <errno.h>
#include <openssl/md5.h>
#include "ts/TsException.h"
#include "tscore/ink_memory.h"
#include "tscore/ink_string.h"

namespace wccp
{
// ------------------------------------------------------
// ------------------------------------------------------
ServiceGroup &
ServiceGroup::setSvcType(ServiceGroup::Type t)
{
  if (STANDARD == t) {
    // For standard service, everything past ID must be zero.
    memset(&m_priority, 0, sizeof(*this) - (reinterpret_cast<char *>(&m_priority) - reinterpret_cast<char *>(this)));
  }
  m_svc_type = t; // store actual type.
  return *this;
}

bool
ServiceGroup::operator==(self const &that) const
{
  if (m_svc_type == STANDARD) {
    // If type are different, fail, else if both are STANDARD
    // then we need only match on the ID.
    return that.m_svc_type == STANDARD && m_svc_id == that.m_svc_id;
  } else if (that.m_svc_type != DYNAMIC) {
    return false;
  } else {
    // Both services are DYNAMIC, check the properties.
    // Port check is technically too strict -- should ignore
    // ports beyond the terminating null port. Oh well.
    return m_svc_id == that.m_svc_id && m_protocol == that.m_protocol && m_flags == that.m_flags && m_priority == that.m_priority &&
           0 == memcmp(m_ports, that.m_ports, sizeof(m_ports));
  }
}
// ------------------------------------------------------
// ------------------------------------------------------
CacheHashIdElt &
CacheHashIdElt::setBucket(int idx, bool state)
{
  uint8_t &bucket = m_buckets[idx >> 3];
  uint8_t mask    = 1 << (idx & 7);
  if (state)
    bucket |= mask;
  else
    bucket &= !mask;
  return *this;
}

CacheHashIdElt &
CacheHashIdElt::setBuckets(bool state)
{
  memset(m_buckets, state ? 0xFF : 0, sizeof(m_buckets));
  return *this;
}

size_t
CacheIdBox::getSize() const
{
  return m_size;
}

CacheIdBox &
CacheIdBox::require(size_t n)
{
  if (m_cap < n) {
    if (m_base && m_cap)
      ats_free(m_base);
    m_base = static_cast<CacheIdElt *>(ats_malloc(n));
    m_cap  = n;
  }
  memset(m_base, 0, m_cap);
  m_size = 0;
  return *this;
}

CacheIdBox &
CacheIdBox::initDefaultHash(uint32_t addr)
{
  this->require(sizeof(CacheHashIdElt));
  m_size = sizeof(CacheHashIdElt);
  m_base->initHashRev().setUnassigned(true).setMask(false).setAddr(addr);
  m_tail           = static_cast<CacheHashIdElt *>(m_base)->getTailPtr();
  m_tail->m_weight = htons(0);
  m_tail->m_status = htons(0);
  return *this;
}

CacheIdBox &
CacheIdBox::initDefaultMask(uint32_t addr)
{
  // Base element plus set with 1 value plus tail.
  this->require(sizeof(CacheMaskIdElt) + MaskValueSetElt::calcSize(1) + sizeof(CacheIdElt::Tail));
  CacheMaskIdElt *mid = static_cast<CacheMaskIdElt *>(m_base);
  mid->initHashRev().setUnassigned(true).setMask(true).setAddr(addr);
  mid->m_assign.init(0, 0, 0, 0)->addValue(addr, 0, 0, 0, 0);
  m_size           = mid->getSize();
  m_tail           = mid->getTailPtr();
  m_tail->m_weight = htons(0);
  m_tail->m_status = htons(0);
  return *this;
}

CacheIdBox &
CacheIdBox::fill(self const &src)
{
  size_t n = src.getSize();
  this->require(src.getSize());
  memcpy(m_base, src.m_base, n);
  m_size = src.m_size;
  // If tail is set in src, use the same offset here.
  if (src.m_tail)
    m_tail = reinterpret_cast<CacheIdElt::Tail *>(reinterpret_cast<char *>(m_base) +
                                                  (reinterpret_cast<char *>(src.m_tail) - reinterpret_cast<char *>(src.m_base)));
  else
    m_tail = 0;
  return *this;
}

CacheIdBox &
CacheIdBox::fill(void *base, self const &src)
{
  m_size = src.getSize();
  m_cap  = 0;
  m_base = static_cast<CacheIdElt *>(base);
  memcpy(m_base, src.m_base, m_size);
  return *this;
}

int
CacheIdBox::parse(MsgBuffer base)
{
  int zret        = PARSE_SUCCESS;
  CacheIdElt *ptr = reinterpret_cast<CacheIdElt *>(base.getTail());
  size_t n        = base.getSpace();
  m_cap           = 0;
  if (ptr->isMask()) {
    CacheMaskIdElt *mptr = static_cast<CacheMaskIdElt *>(ptr);
    size_t size          = sizeof(CacheMaskIdElt);
    // Sanity check - verify enough room for empty elements.
    if (n < size || n < size + MaskValueSetElt::calcSize(0) * mptr->getCount()) {
      zret = PARSE_BUFFER_TOO_SMALL;
    } else {
      m_size = mptr->getSize();
      if (n < m_size) {
        zret = PARSE_BUFFER_TOO_SMALL;
        logf(LVL_DEBUG, "I_SEE_YOU Cache Mask ID too small: %lu < %lu", n, m_size);
      } else {
        m_tail = mptr->getTailPtr();
      }
    }
  } else {
    if (n < sizeof(CacheHashIdElt)) {
      zret = PARSE_BUFFER_TOO_SMALL;
      logf(LVL_DEBUG, "I_SEE_YOU Cache Hash ID too small: %lu < %lu", n, sizeof(CacheHashIdElt));
    } else {
      m_size = sizeof(CacheHashIdElt);
      m_tail = static_cast<CacheHashIdElt *>(m_base)->getTailPtr();
    }
  }
  if (PARSE_SUCCESS == zret)
    m_base = ptr;
  return zret;
}
// ------------------------------------------------------
inline CapabilityElt::Type
CapabilityElt::getCapType() const
{
  return static_cast<Type>(ntohs(m_cap_type));
}

inline CapabilityElt &
CapabilityElt::setCapType(Type cap)
{
  m_cap_type = static_cast<Type>(htons(cap));
  return *this;
}

inline uint32_t
CapabilityElt::getCapData() const
{
  return ntohl(m_cap_data);
}

inline CapabilityElt &
CapabilityElt::setCapData(uint32_t data)
{
  m_cap_data = htonl(data);
  return *this;
}

CapabilityElt::CapabilityElt() {}

CapabilityElt::CapabilityElt(Type cap, uint32_t data)
{
  this->setCapType(cap);
  this->setCapData(data);
  m_cap_length = htons(sizeof(uint32_t));
}
// ------------------------------------------------------
inline uint32_t
ValueElt::getf_src_addr() const
{
  return ntohl(m_src_addr);
}

inline ValueElt &
ValueElt::setf_src_addr(uint32_t addr)
{
  m_src_addr = htonl(addr);
  return *this;
}

inline uint32_t
ValueElt::getDstAddr() const
{
  return ntohl(m_dst_addr);
}

inline ValueElt &
ValueElt::setf_dst_addr(uint32_t addr)
{
  m_dst_addr = htonl(addr);
  return *this;
}

inline uint16_t
ValueElt::getf_src_port() const
{
  return ntohs(m_src_port);
}

inline ValueElt &
ValueElt::setf_src_port(uint16_t port)
{
  m_src_port = htons(port);
  return *this;
}

inline uint16_t
ValueElt::getDstPort() const
{
  return ntohs(m_dst_port);
}

inline ValueElt &
ValueElt::setf_dst_port(uint16_t port)
{
  m_dst_port = htons(port);
  return *this;
}

inline uint32_t
ValueElt::getCacheAddr() const
{
  return ntohl(m_cache_addr);
}

inline ValueElt &
ValueElt::setCacheAddr(uint32_t addr)
{
  m_cache_addr = htonl(addr);
  return *this;
}
// ------------------------------------------------------
MaskValueSetElt &
MaskValueSetElt::addValue(uint32_t cacheAddr, uint32_t srcAddr, uint32_t dstAddr, uint16_t srcPort, uint16_t dstPort)
{
  uint32_t idx = ntohl(m_count);
  new (this->values() + idx) ValueElt(cacheAddr, srcAddr, dstAddr, srcPort, dstPort);
  m_count = htonl(idx + 1);
  return *this;
}

size_t
MaskAssignElt::getVarSize() const
{
  size_t zret = 0;
  int n       = this->getCount();

  MaskValueSetElt const *set = reinterpret_cast<MaskValueSetElt const *>(this + 1);
  while (n--) {
    size_t k = set->getSize();
    zret += k;
    set = reinterpret_cast<MaskValueSetElt const *>(reinterpret_cast<char const *>(set) + k);
  }
  return zret;
}

HashAssignElt &
HashAssignElt::round_robin_assign()
{
  uint32_t v_caches = this->getCount();
  Bucket *buckets   = this->getBucketBase();
  if (1 == v_caches)
    memset(buckets, 0, sizeof(Bucket) * N_BUCKETS);
  else { // Assign round robin.
    size_t x = 0;
    for (Bucket *spot = buckets, *limit = spot + N_BUCKETS; spot < limit; ++spot) {
      spot->m_idx = x;
      spot->m_alt = 0;
      x           = (x + 1) % v_caches;
    }
  }
  return *this;
}

RouterAssignListElt &
RouterAssignListElt::updateRouterId(uint32_t addr, uint32_t rcvid, uint32_t cno)
{
  uint32_t n           = this->getCount();
  RouterAssignElt *elt = access_array<RouterAssignElt>(this + 1);
  for (uint32_t i = 0; i < n; ++i, ++elt) {
    if (addr == elt->getAddr()) {
      elt->setChangeNumber(cno).setRecvId(rcvid);
      break;
    }
  }
  return *this;
}
// ------------------------------------------------------
message_type_t
MsgHeaderComp::getType()
{
  return static_cast<message_type_t>(get_field(&raw_t::m_type, m_base));
}

uint16_t
MsgHeaderComp::getVersion()
{
  return get_field(&raw_t::m_version, m_base);
}

uint16_t
MsgHeaderComp::getLength()
{
  return get_field(&raw_t::m_length, m_base);
}

MsgHeaderComp &
MsgHeaderComp::setType(message_type_t type)
{
  set_field(&raw_t::m_type, m_base, type);
  return *this;
}

MsgHeaderComp &
MsgHeaderComp::setVersion(uint16_t version)
{
  set_field(&raw_t::m_version, m_base, version);
  return *this;
}

MsgHeaderComp &
MsgHeaderComp::setLength(uint16_t length)
{
  set_field(&raw_t::m_length, m_base, length);
  return *this;
}

size_t
MsgHeaderComp::calcSize()
{
  return sizeof(raw_t);
}

MsgHeaderComp &
MsgHeaderComp::fill(MsgBuffer &buffer, message_type_t t)
{
  size_t comp_size = this->calcSize();
  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);
  m_base = buffer.getTail();
  buffer.use(comp_size);
  this->setType(t).setVersion(VERSION).setLength(0);
  return *this;
}

int
MsgHeaderComp::parse(MsgBuffer &base)
{
  int zret         = PARSE_SUCCESS;
  size_t comp_size = this->calcSize();
  if (base.getSpace() < comp_size) {
    zret = PARSE_BUFFER_TOO_SMALL;
  } else {
    m_base = base.getTail();
    // Length field puts end of message past end of buffer.
    if (this->getLength() + comp_size > base.getSpace()) {
      zret = PARSE_MSG_TOO_BIG;
    } else if (INVALID_MSG_TYPE == this->toMsgType(get_field(&raw_t::m_type, m_base))) {
      zret = PARSE_COMP_TYPE_INVALID;
    } else {
      base.use(comp_size);
    }
  }
  return zret;
}
// ------------------------------------------------------
SecurityComp::Key SecurityComp::m_default_key;
SecurityComp::Option SecurityComp::m_default_opt = SECURITY_NONE;

SecurityComp::Option
SecurityComp::getOption() const
{
  return static_cast<Option>(get_field(&RawNone::m_option, m_base));
}

SecurityComp &
SecurityComp::setOption(Option opt)
{
  set_field(&RawNone::m_option, m_base, static_cast<uint32_t>(opt));
  return *this;
}

SecurityComp &
SecurityComp::setKey(char const *key)
{
  m_local_key = true;
  ink_strlcpy(m_key, key, KEY_SIZE);
  return *this;
}

void
SecurityComp::setDefaultKey(char const *key)
{
  ink_strlcpy(m_default_key, key, KEY_SIZE);
}

SecurityComp &
SecurityComp::fill(MsgBuffer &buffer, Option opt)
{
  size_t comp_size = this->calcSize(opt);

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE).setLength(comp_size - sizeof(super::raw_t)).setOption(opt);

  if (SECURITY_NONE != opt) {
    RawMD5::HashData &data = access_field(&RawMD5::m_data, m_base);
    memset(data, 0, sizeof(data));
  }

  buffer.use(comp_size);
  return *this;
}

SecurityComp &
SecurityComp::secure(MsgBuffer const &msg)
{
  if (SECURITY_MD5 == this->getOption()) {
    MD5_CTX ctx;
    char const *key = m_local_key ? m_key : m_default_key;
    MD5_Init(&ctx);
    MD5_Update(&ctx, key, KEY_SIZE);
    MD5_Update(&ctx, msg.getBase(), msg.getCount());
    MD5_Final(access_field(&RawMD5::m_data, m_base), &ctx);
  }
  return *this;
}

bool
SecurityComp::validate(MsgBuffer const &msg) const
{
  bool zret = true;
  if (SECURITY_MD5 == this->getOption()) {
    RawMD5::HashData save;
    RawMD5::HashData &org = const_cast<RawMD5::HashData &>(access_field(&RawMD5::m_data, m_base));
    MD5_CTX ctx;
    char const *key = m_local_key ? m_key : m_default_key;
    // save the original hash aside.
    memcpy(save, org, sizeof(save));
    // zero out the component hash area to compute the hash.
    memset(org, 0, sizeof(org));
    // Compute hash in to hash area.
    MD5_Init(&ctx);
    MD5_Update(&ctx, key, KEY_SIZE);
    MD5_Update(&ctx, msg.getBase(), msg.getCount());
    MD5_Final(org, &ctx);
    // check hash.
    zret = 0 == memcmp(org, save, sizeof(save));
    // restore original data.
    memcpy(org, save, sizeof(save));
  }
  return zret;
}

int
SecurityComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      Option opt = this->getOption();
      if (SECURITY_NONE != opt && SECURITY_MD5 != opt)
        zret = PARSE_COMP_INVALID;
      else {
        size_t comp_size = this->calcSize(opt);
        if (this->getLength() != comp_size - sizeof(super::raw_t))
          zret = PARSE_COMP_WRONG_SIZE;
        else
          buffer.use(comp_size);
      }
    }
  }
  return zret;
}
// ------------------------------------------------------

ServiceComp &
ServiceComp::setPort(int idx, uint16_t port)
{
  this->access()->setPort(idx, port);
  m_port_count = std::max(m_port_count, idx);
  return *this;
}

ServiceComp &
ServiceComp::addPort(uint16_t port)
{
  if (m_port_count < static_cast<int>(ServiceGroup::N_PORTS))
    this->access()->setPort(m_port_count++, port);
  return *this;
}

ServiceComp &
ServiceComp::clearPorts()
{
  this->access()->clearPorts();
  m_port_count = 0;
  return *this;
}

ServiceComp &
ServiceComp::fill(MsgBuffer &buffer, ServiceGroup const &svc)
{
  size_t comp_size = this->calcSize();

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE).setLength(comp_size - sizeof(super::raw_t));

  // Cast buffer to our serialized type, then cast to ServiceGroup
  // to get offset of that part of the serialization storage.
  memcpy(
    // This generates a gcc warning, but the next line doesn't. Yay.
    //    static_cast<ServiceGroup*>(reinterpret_cast<raw_t*>(m_base)),
    &static_cast<ServiceGroup &>(*reinterpret_cast<raw_t *>(m_base)), &svc, sizeof(svc));
  buffer.use(comp_size);
  return *this;
}

int
ServiceComp::parse(MsgBuffer &buffer)
{
  int zret         = PARSE_SUCCESS;
  size_t comp_size = this->calcSize();
  if (buffer.getSpace() < comp_size)
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      ServiceGroup::Type svc = this->getSvcType();
      if (ServiceGroup::DYNAMIC != svc && ServiceGroup::STANDARD != svc)
        zret = PARSE_COMP_INVALID;
      else if (this->getLength() != comp_size - sizeof(super::raw_t))
        zret = PARSE_COMP_WRONG_SIZE;
      else
        buffer.use(comp_size);
    }
  }
  return zret;
}
// ------------------------------------------------------
RouterIdElt &
RouterIdComp::idElt()
{
  return access_field(&raw_t::m_id, m_base);
}

RouterIdElt const &
RouterIdComp::idElt() const
{
  return access_field(&raw_t::m_id, m_base);
}

RouterIdComp &
RouterIdComp::setIdElt(uint32_t addr, uint32_t recv_id)
{
  this->idElt().setAddr(addr).setRecvId(recv_id);
  return *this;
}

uint32_t
RouterIdComp::getAddr() const
{
  return this->idElt().getAddr();
}

RouterIdComp &
RouterIdComp::setAddr(uint32_t addr)
{
  this->idElt().setAddr(addr);
  return *this;
}

uint32_t
RouterIdComp::getRecvId() const
{
  return this->idElt().getRecvId();
}
inline RouterIdComp &
RouterIdComp::setRecvId(uint32_t id)
{
  this->idElt().setRecvId(id);
  return *this;
}

uint32_t
RouterIdComp::getToAddr() const
{
  return access_field(&raw_t::m_to_addr, m_base);
}

RouterIdComp &
RouterIdComp::setToAddr(uint32_t addr)
{
  access_field(&raw_t::m_to_addr, m_base) = addr;
  return *this;
}

uint32_t
RouterIdComp::getFromCount() const
{
  return get_field(&raw_t::m_from_count, m_base);
}

uint32_t
RouterIdComp::getFromAddr(int idx) const
{
  return access_array<uint32_t>(m_base + sizeof(raw_t))[idx];
}

RouterIdComp &
RouterIdComp::setFromAddr(int idx, uint32_t addr)
{
  access_array<uint32_t>(m_base + sizeof(raw_t))[idx] = addr;
  return *this;
}

int
RouterIdComp::findFromAddr(uint32_t addr)
{
  int n           = this->getFromCount();
  uint32_t *addrs = access_array<uint32_t>(m_base + sizeof(raw_t)) + n;
  while (n-- != 0 && *--addrs != addr)
    ;
  return n;
}

RouterIdComp &
RouterIdComp::fill(MsgBuffer &buffer, size_t n_caches)
{
  size_t comp_size = this->calcSize(n_caches);
  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE);
  set_field(&raw_t::m_from_count, m_base, n_caches);
  this->setLength(comp_size - sizeof(super::raw_t));
  buffer.use(comp_size);

  return *this;
}

RouterIdComp &
RouterIdComp::fillSingleton(MsgBuffer &buffer, uint32_t addr, uint32_t recv_count, uint32_t to_addr, uint32_t from_addr)
{
  size_t comp_size = this->calcSize(1);

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE).setIdElt(addr, recv_count).setToAddr(to_addr).setFromAddr(0, from_addr);

  set_field(&raw_t::m_from_count, m_base, 1);

  this->setLength(comp_size - sizeof(super::raw_t));
  buffer.use(comp_size);

  return *this;
}

int
RouterIdComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      size_t comp_size = this->calcSize(this->getFromCount());
      if (this->getLength() != comp_size - sizeof(super::raw_t))
        zret = PARSE_COMP_WRONG_SIZE;
      else
        buffer.use(comp_size);
    }
  }
  return zret;
}
// ------------------------------------------------------
AssignmentKeyElt &
RouterViewComp::keyElt()
{
  return access_field(&raw_t::m_key, m_base);
}

AssignmentKeyElt const &
RouterViewComp::keyElt() const
{
  return access_field(&raw_t::m_key, m_base);
}

uint32_t
RouterViewComp::getChangeNumber() const
{
  return get_field(&raw_t::m_change_number, m_base);
}

RouterViewComp &
RouterViewComp::setChangeNumber(uint32_t n)
{
  set_field(&raw_t::m_change_number, m_base, n);
  return *this;
}

// This is untainted because an overall size check is done when the packet is read. If any of the
// counts are bogus, that size check will fail.
// coverity[ -tainted_data_return]
uint32_t
RouterViewComp::getCacheCount() const
{
  return ntohl(*m_cache_count);
}

// This is untainted because an overall size check is done when the packet is read. If any of the
// counts are bogus, that size check will fail.
// coverity[ -tainted_data_return]
uint32_t
RouterViewComp::getRouterCount() const
{
  return get_field(&raw_t::m_router_count, m_base);
}

CacheIdBox &
RouterViewComp::cacheId(int idx)
{
  return m_cache_ids[idx];
}

uint32_t
RouterViewComp::getRouterAddr(int idx) const
{
  return access_array<uint32_t>(m_base + sizeof(raw_t))[idx];
}

RouterViewComp &
RouterViewComp::setRouterAddr(int idx, uint32_t addr)
{
  access_array<uint32_t>(m_base + sizeof(raw_t))[idx] = addr;
  return *this;
}

uint32_t *
RouterViewComp::calc_cache_count_ptr()
{
  return reinterpret_cast<uint32_t *>(m_base + sizeof(raw_t) + this->getRouterCount() * sizeof(uint32_t));
}

RouterViewComp &
RouterViewComp::fill(MsgBuffer &buffer, int n_routers, int n_caches)
{
  // TBD: This isn't right since the upgrade to mask support
  // because the size isn't a static function of the router and
  // cache count anymore.
  size_t comp_size = sizeof(raw_t);

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE);

  // No setf for this field, hand pack it.
  set_field(&raw_t::m_router_count, m_base, n_routers);
  // Set the pointer to the count of caches.
  m_cache_count = this->calc_cache_count_ptr();
  // No setf, go direct.
  *m_cache_count = htonl(n_caches);

  this->setLength(comp_size - HEADER_SIZE);
  buffer.use(comp_size);

  return *this;
}

int
RouterViewComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      uint32_t ncaches; // # of caches.
      if (this->getRouterCount() > MAX_ROUTERS)
        zret = PARSE_MSG_INVALID;
      // check if cache count is past end of buffer
      else if (static_cast<void *>(m_cache_count = this->calc_cache_count_ptr()) >=
               static_cast<void *>(buffer.getBase() + buffer.getSize()))
        zret = PARSE_COMP_WRONG_SIZE, log(LVL_DEBUG, "I_SEE_YOU: cache counter past end of buffer");
      else if ((ncaches = this->getCacheCount()) > MAX_CACHES)
        zret = PARSE_MSG_INVALID;
      else {
        size_t comp_size = reinterpret_cast<char *>(m_cache_count + 1) - m_base;
        // Walk the cache ID elements.
        MsgBuffer spot(buffer);
        CacheIdBox *box = m_cache_ids;
        uint32_t idx    = 0;
        spot.use(comp_size);
        while (idx < ncaches && PARSE_SUCCESS == (zret = box->parse(spot))) {
          size_t k = box->getSize();
          spot.use(k);
          comp_size += k;
          ++box;
          ++idx;
        }
        if (PARSE_SUCCESS == zret)
          buffer.use(comp_size);
      }
    }
  }
  return zret;
}
// ------------------------------------------------------
CacheIdComp &
CacheIdComp::fill(MsgBuffer &base, CacheIdBox const &src)
{
  size_t comp_size = src.getSize() + HEADER_SIZE;

  if (base.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = base.getTail();
  this->setType(COMP_TYPE).setLength(comp_size - HEADER_SIZE);
  m_box.fill(&(access_field(&raw_t::m_id, m_base)), src);
  base.use(comp_size);
  return *this;
}

int
CacheIdComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      MsgBuffer tmp(buffer);
      tmp.use(reinterpret_cast<char *>(&(access_field(&raw_t::m_id, m_base))) - m_base);
      zret = m_box.parse(tmp);
      if (PARSE_SUCCESS == zret) {
        size_t comp_size = HEADER_SIZE + m_box.getSize();
        if (this->getLength() != comp_size - HEADER_SIZE)
          zret = PARSE_COMP_WRONG_SIZE;
        else
          buffer.use(comp_size);
      }
    }
  }
  return zret;
}
// ------------------------------------------------------
uint32_t
CacheViewComp::getChangeNumber() const
{
  return get_field(&raw_t::m_change_number, m_base);
}

CacheViewComp &
CacheViewComp::setChangeNumber(uint32_t n)
{
  set_field(&raw_t::m_change_number, m_base, n);
  return *this;
}

uint32_t
CacheViewComp::getRouterCount() const
{
  return get_field(&raw_t::m_router_count, m_base);
}

uint32_t
CacheViewComp::getCacheCount() const
{
  return ntohl(*m_cache_count);
}

uint32_t
CacheViewComp::getCacheAddr(int idx) const
{
  return ntohl(m_cache_count[idx + 1]);
}

CacheViewComp &
CacheViewComp::setCacheAddr(int idx, uint32_t addr)
{
  m_cache_count[idx + 1] = addr;
  return *this;
}

RouterIdElt *
CacheViewComp::atf_router_array()
{
  return reinterpret_cast<RouterIdElt *>(m_base + sizeof(raw_t));
}

RouterIdElt &
CacheViewComp::routerElt(int idx)
{
  return this->atf_router_array()[idx];
}

RouterIdElt *
CacheViewComp::findf_router_elt(uint32_t addr)
{
  for (RouterIdElt *rtr = this->atf_router_array(), *limit = rtr + this->getRouterCount(); rtr < limit; ++rtr) {
    if (rtr->getAddr() == addr)
      return rtr;
  }
  return 0;
}

size_t
CacheViewComp::calcSize(int n_routers, int n_caches)
{
  return sizeof(raw_t) + n_routers * sizeof(RouterIdElt) + sizeof(uint32_t) + n_caches * sizeof(uint32_t);
}

CacheViewComp &
CacheViewComp::fill(MsgBuffer &buffer, detail::cache::GroupData const &group)
{
  int i;
  size_t n_routers = group.m_routers.size();
  size_t n_caches  = group.m_caches.size();
  size_t comp_size = this->calcSize(n_routers, n_caches);

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE).setChangeNumber(group.m_generation);

  set_field(&raw_t::m_router_count, m_base, n_routers);
  // Set the pointer to the count of caches.
  m_cache_count  = reinterpret_cast<uint32_t *>(m_base + sizeof(raw_t) + n_routers * sizeof(RouterIdElt));
  *m_cache_count = htonl(n_caches); // set the actual count.

  // Fill routers.
  i = 0;
  for (detail::cache::RouterBag::const_iterator spot = group.m_routers.begin(), limit = group.m_routers.end(); spot != limit;
       ++spot, ++i) {
    this->routerElt(i).setAddr(spot->m_addr).setRecvId(spot->m_recv.m_sn);
  }

  // fill caches.
  i = 0;
  for (detail::cache::CacheBag::const_iterator spot = group.m_caches.begin(), limit = group.m_caches.end(); spot != limit;
       ++spot, ++i) {
    this->setCacheAddr(i, spot->idAddr());
  }

  this->setLength(comp_size - sizeof(super::raw_t));
  buffer.use(comp_size);
  return *this;
}

int
CacheViewComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      m_cache_count    = reinterpret_cast<uint32_t *>(m_base + sizeof(raw_t) + this->getRouterCount() * sizeof(RouterIdElt));
      size_t comp_size = this->calcSize(this->getRouterCount(), this->getCacheCount());
      if (this->getLength() != comp_size - sizeof(super::raw_t))
        zret = PARSE_COMP_WRONG_SIZE;
      else
        buffer.use(comp_size);
    }
  }
  return zret;
}
// ------------------------------------------------------
AssignmentKeyElt &
AssignInfoComp::keyElt()
{
  return access_field(&raw_t::m_key, m_base);
}

AssignmentKeyElt const &
AssignInfoComp::keyElt() const
{
  return access_field(&raw_t::m_key, m_base);
}

uint32_t
AssignInfoComp::getKeyChangeNumber() const
{
  return access_field(&raw_t::m_key, m_base).getChangeNumber();
}

AssignInfoComp &
AssignInfoComp::setKeyChangeNumber(uint32_t n)
{
  access_field(&raw_t::m_key, m_base).setChangeNumber(n);
  return *this;
}

uint32_t
AssignInfoComp::getKeyAddr() const
{
  return access_field(&raw_t::m_key, m_base).getAddr();
}

AssignInfoComp &
AssignInfoComp::setKeyAddr(uint32_t addr)
{
  access_field(&raw_t::m_key, m_base).setAddr(addr);
  return *this;
}

uint32_t
AssignInfoComp::getRouterCount() const
{
  return access_field(&raw_t::m_routers, m_base).getCount();
}

RouterAssignElt &
AssignInfoComp::routerElt(int idx)
{
  return access_field(&raw_t::m_routers, m_base).elt(idx);
}

uint32_t
AssignInfoComp::getCacheCount() const
{
  return ntohl(*m_cache_count);
}

uint32_t
AssignInfoComp::getCacheAddr(int idx) const
{
  return m_cache_count[idx + 1];
}

AssignInfoComp &
AssignInfoComp::setCacheAddr(int idx, uint32_t addr)
{
  m_cache_count[idx + 1] = addr;
  return *this;
}

size_t
AssignInfoComp::calcSize(int n_routers, int n_caches)
{
  return sizeof(raw_t) + RouterAssignListElt::calcVarSize(n_routers) + HashAssignElt::calcSize(n_caches);
}

uint32_t *
AssignInfoComp::calcCacheCountPtr()
{
  return reinterpret_cast<uint32_t *>(m_base + sizeof(raw_t) + access_field(&raw_t::m_routers, m_base).getVarSize());
}

AssignInfoComp::Bucket *
AssignInfoComp::calcBucketPtr()
{
  return reinterpret_cast<Bucket *>(reinterpret_cast<char *>(m_cache_count) + sizeof(uint32_t) * (1 + this->getCacheCount()));
}

AssignInfoComp &
AssignInfoComp::fill(MsgBuffer &buffer, detail::Assignment const &assign)
{
  RouterAssignListElt const &ralist = assign.getRouterList();
  HashAssignElt const &ha           = assign.getHash();
  size_t n_routers                  = ralist.getCount();
  size_t n_caches                   = ha.getCount();
  size_t comp_size                  = this->calcSize(n_routers, n_caches);

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE);
  this->keyElt() = assign.getKey();
  memcpy(&(access_field(&raw_t::m_routers, m_base)), &ralist, ralist.getSize());
  // Set the pointer to the count of caches and write the count.
  m_cache_count = this->calcCacheCountPtr();
  memcpy(m_cache_count, &ha, ha.getSize());

  this->setLength(comp_size - HEADER_SIZE);
  buffer.use(comp_size);

  return *this;
}

int
AssignInfoComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < HEADER_SIZE)
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      int n_routers = this->getRouterCount();
      int n_caches;
      m_cache_count    = this->calcCacheCountPtr();
      n_caches         = this->getCacheCount();
      m_buckets        = this->calcBucketPtr();
      size_t comp_size = this->calcSize(n_routers, n_caches);
      if (this->getLength() != comp_size - HEADER_SIZE)
        zret = PARSE_COMP_WRONG_SIZE;
      else
        buffer.use(comp_size);
    }
  }
  if (PARSE_SUCCESS != zret)
    m_base = 0;
  return zret;
}

AssignmentKeyElt &
AltAssignComp::keyElt()
{
  return access_field(&raw_t::m_key, m_base);
}

AssignmentKeyElt const &
AltAssignComp::keyElt() const
{
  return access_field(&raw_t::m_key, m_base);
}

void *
AltAssignComp::calcVarPtr()
{
  return reinterpret_cast<void *>(m_base + sizeof(raw_t) + access_field(&raw_t::m_routers, m_base).getVarSize());
}

uint32_t
AltAssignComp::getRouterCount() const
{
  return access_field(&raw_t::m_routers, m_base).getCount();
}

uint32_t
AltHashAssignComp::getCacheCount() const
{
  return ntohl(*m_cache_count);
}

size_t
AltHashAssignComp::calcSize(int n_routers, int n_caches)
{
  return sizeof(raw_t) + RouterAssignListElt::calcVarSize(n_routers) + HashAssignElt::calcSize(n_caches);
}

AltHashAssignComp &
AltHashAssignComp::fill(MsgBuffer &buffer, detail::Assignment const &assign)
{
  RouterAssignListElt const &ralist = assign.getRouterList();
  HashAssignElt const &ha           = assign.getHash();
  size_t n_routers                  = ralist.getCount();
  size_t n_caches                   = ha.getCount();
  size_t comp_size                  = this->calcSize(n_routers, n_caches);

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE)
    .setLength(comp_size - HEADER_SIZE)
    .setAssignType(ALT_HASH_ASSIGNMENT)
    .setAssignLength(comp_size - HEADER_SIZE - sizeof(local_header_t));
  this->keyElt() = assign.getKey();
  memcpy(&(access_field(&raw_t::m_routers, m_base)), &ralist, ralist.getSize());
  // Set the pointer to the count of caches and write the count.
  m_cache_count = static_cast<uint32_t *>(this->calcVarPtr());
  memcpy(m_cache_count, &ha, ha.getSize());

  buffer.use(comp_size);

  return *this;
}

int
AltHashAssignComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      int n_routers = this->getRouterCount();
      int n_caches;
      m_cache_count    = static_cast<uint32_t *>(this->calcVarPtr());
      n_caches         = this->getCacheCount();
      size_t comp_size = this->calcSize(n_routers, n_caches);
      if (this->getLength() != comp_size - HEADER_SIZE)
        zret = PARSE_COMP_WRONG_SIZE;
      else
        buffer.use(comp_size);
    }
  }
  if (PARSE_SUCCESS != zret)
    m_base = 0;
  return zret;
}

AltMaskAssignComp &
AltMaskAssignComp::fill(MsgBuffer &buffer, detail::Assignment const &assign)
{
  RouterAssignListElt const &ralist = assign.getRouterList();
  MaskAssignElt const &ma           = assign.getMask();
  size_t comp_size                  = sizeof(raw_t) + ralist.getVarSize() + ma.getSize();

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE)
    .setLength(comp_size - HEADER_SIZE)
    .setAssignType(ALT_MASK_ASSIGNMENT)
    .setAssignLength(comp_size - HEADER_SIZE - sizeof(local_header_t));
  this->keyElt() = assign.getKey();

  memcpy(&(access_field(&raw_t::m_routers, m_base)), &ralist, ralist.getSize());
  m_mask_elt = static_cast<MaskAssignElt *>(this->calcVarPtr());
  memcpy(m_mask_elt, &ma, ma.getSize());

  buffer.use(comp_size);

  return *this;
}

int
AltMaskAssignComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      RouterAssignListElt *ralist = &(access_field(&raw_t::m_routers, m_base));
      m_mask_elt                  = static_cast<MaskAssignElt *>(this->calcVarPtr());
      size_t comp_size            = sizeof(raw_t) + ralist->getVarSize() + m_mask_elt->getSize();
      if (this->getLength() != comp_size - HEADER_SIZE)
        zret = PARSE_COMP_WRONG_SIZE;
      else
        buffer.use(comp_size);
    }
  }
  if (PARSE_SUCCESS != zret)
    m_base = 0;
  return zret;
}

// ------------------------------------------------------
CmdComp::cmd_t
CmdComp::getCmd() const
{
  return static_cast<cmd_t>(get_field(&raw_t::m_cmd, m_base));
}

CmdComp &
CmdComp::setCmd(cmd_t cmd)
{
  set_field(&raw_t::m_cmd, m_base, cmd);
  return *this;
}

uint32_t
CmdComp::getCmdData() const
{
  return get_field(&raw_t::m_cmd_data, m_base);
}

CmdComp &
CmdComp::setCmdData(uint32_t data)
{
  set_field(&raw_t::m_cmd_data, m_base, data);
  return *this;
}

inline size_t
CmdComp::calcSize()
{
  return sizeof(raw_t);
}

CmdComp &
CmdComp::fill(MsgBuffer &buffer, cmd_t cmd, uint32_t data)
{
  size_t comp_size = this->calcSize();

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();

  this->setType(COMP_TYPE).setCmd(cmd).setCmdData(data).setLength(sizeof(raw_t) - sizeof(super::raw_t));
  // Command length is always the same.
  set_field(&raw_t::m_length, m_base, sizeof(uint32_t));
  //  reinterpret_cast<raw_t*>(m_base)->m_length = htons(sizeof(uint32_t));
  return *this;
}

int
CmdComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      if (this->getLength() + sizeof(super::raw_t) != this->calcSize())
        zret = PARSE_COMP_WRONG_SIZE;
    }
  }
  return zret;
}
// ------------------------------------------------------
CapabilityElt &
CapComp::elt(int idx)
{
  return access_array<CapabilityElt>(m_base + sizeof(super::raw_t))[idx];
}

CapabilityElt const &
CapComp::elt(int idx) const
{
  return access_array<CapabilityElt>(m_base + sizeof(super::raw_t))[idx];
}

void
CapComp::cache() const
{
  uint32_t x; // scratch for bounds checking.
  // Reset all values.
  m_packet_forward = ServiceGroup::NO_PACKET_STYLE;
  m_packet_return  = ServiceGroup::NO_PACKET_STYLE;
  m_cache_assign   = ServiceGroup::NO_CACHE_ASSIGN_STYLE;
  if (!m_base)
    return; // No data, everything is default.
  // Load from data.
  for (uint32_t i = 0, n = this->getEltCount(); i < n; ++i) {
    CapabilityElt const &elt = this->elt(i);
    switch (elt.getCapType()) {
    case CapabilityElt::PACKET_FORWARD_METHOD:
      x = elt.getCapData();
      if (0 < x && x < 4)
        m_packet_forward = static_cast<ServiceGroup::PacketStyle>(x);
      break;
    case CapabilityElt::PACKET_RETURN_METHOD:
      x = elt.getCapData();
      if (0 < x && x < 4)
        m_packet_return = static_cast<ServiceGroup::PacketStyle>(x);
      break;
    case CapabilityElt::CACHE_ASSIGNMENT_METHOD:
      x = elt.getCapData();
      if (0 < x && x < 4)
        m_cache_assign = static_cast<ServiceGroup::CacheAssignmentStyle>(x);
      break;
    default:
      logf(LVL_INFO, "Invalid capability type %d in packet.", elt.getCapType());
      break;
    }
  }
  m_cached = true;
}

CapComp &
CapComp::fill(MsgBuffer &buffer, int n)
{
  size_t comp_size = this->calcSize(n);
  m_cached         = false;

  if (buffer.getSpace() < comp_size)
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();
  this->setType(COMP_TYPE).setLength(comp_size - sizeof(super::raw_t));
  m_count = n;
  buffer.use(comp_size);

  return *this;
}

int
CapComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  m_cached = false;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      // No explicit count, compute it from length.
      m_count = this->getLength() / sizeof(CapabilityElt);
      buffer.use(this->getLength() + sizeof(super::raw_t));
    }
  }
  return zret;
}
// ------------------------------------------------------
int
QueryComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < sizeof(raw_t))
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret)
      buffer.use(this->calcSize());
  }
  return zret;
}
// ------------------------------------------------------
uint32_t
AssignMapComp::getCount() const
{
  return access_field(&raw_t::m_assign, m_base).getCount();
}

AssignMapComp &
AssignMapComp::fill(MsgBuffer &buffer, detail::Assignment const &assign)
{
  size_t comp_size        = sizeof(raw_t);
  MaskAssignElt const &ma = assign.getMask();
  size_t ma_size          = ma.getSize(); // Not constant time.

  // Can't be precise, but we need at least one mask/value set with
  // at least one value. If we don't have that it's a clear fail.
  if (buffer.getSpace() < comp_size + sizeof(MaskValueSetElt::calcSize(1)))
    throw ts::Exception(BUFFER_TOO_SMALL_FOR_COMP_TEXT);

  m_base = buffer.getTail();
  memcpy(&(access_field(&raw_t::m_assign, m_base)), &ma, ma_size);
  comp_size += ma_size - sizeof(ma);

  this->setType(COMP_TYPE).setLength(comp_size - HEADER_SIZE);
  buffer.use(comp_size);

  return *this;
}

int
AssignMapComp::parse(MsgBuffer &buffer)
{
  int zret = PARSE_SUCCESS;
  if (buffer.getSpace() < HEADER_SIZE)
    zret = PARSE_BUFFER_TOO_SMALL;
  else {
    m_base = buffer.getTail();
    zret   = this->checkHeader(buffer, COMP_TYPE);
    if (PARSE_SUCCESS == zret) {
      // TBD - Actually check the mask/value set data !!
      buffer.use(this->getLength() + HEADER_SIZE);
    }
  }
  if (PARSE_SUCCESS != zret)
    m_base = 0;
  return zret;
}
// ------------------------------------------------------
detail::Assignment::Assignment() : m_key(0, 0), m_active(false), m_router_list(0), m_hash_assign(0), m_mask_assign(0) {}

bool
detail::Assignment::fill(cache::GroupData &group, uint32_t addr)
{
  // Compute the last packet received times for the routers.  For each
  // cache, compute how many routers mentioned it in their last
  // packet. Prepare an assignment from those caches.  If there are no
  // such caches, fail the assignment.  Any cache that wasn't
  // mentioned by at least one router are purged.

  size_t n_routers = group.m_routers.size(); // routers in group
  size_t n_caches  = group.m_caches.size();  // caches in group

  // We need both routers and caches to do something useful.
  if (!(n_routers && n_caches))
    return false;

  // Iteration vars.
  size_t cdx, rdx;
  cache::RouterBag::iterator rspot, rbegin = group.m_routers.begin(), rend = group.m_routers.end();
  cache::CacheBag::iterator cspot, cbegin = group.m_caches.begin(), cend = group.m_caches.end();

  size_t nr[n_caches];       // validity check counts.
  memset(nr, 0, sizeof(nr)); // Set counts to zero.

  // Guess at size of serialization buffer. For the router list and
  // the hash assignment, we can compute reasonable upper bounds and so
  // we don't have to do space checks when those get filled out.
  // The mask assignment is more difficult. We just guess generously and
  // try to recover if we go over.
  size_t size = RouterAssignListElt::calcSize(n_routers) + HashAssignElt::calcSize(n_caches) + 4096;

  if (m_buffer.getSize() < size) {
    ats_free(m_buffer.getBase());
    m_buffer.set(ats_malloc(size), size);
  }
  m_buffer.reset();

  // Set assignment key
  m_key.setAddr(addr).setChangeNumber(group.m_generation);

  m_router_list = reinterpret_cast<RouterAssignListElt *>(m_buffer.getBase());
  new (m_router_list) RouterAssignListElt(n_routers);

  // For each router, update the assignment and run over the caches
  // and bump the nr count if that cache was included in the most
  // recent packet. Note that the router data gets updated from
  // ISeeYou message processing as well, this is more of an initial
  // setup.
  for (rdx = 0, rspot = rbegin; rspot != rend; ++rspot, ++rdx) {
    // Update router assignment.
    m_router_list->elt(rdx).setChangeNumber(rspot->m_generation).setAddr(rspot->m_addr).setRecvId(rspot->m_recv.m_sn);
    // Check cache validity.
    for (cdx = 0, cspot = cbegin; cspot != cend; ++cspot, ++cdx) {
      if (cspot->m_src[rdx].m_time == rspot->m_recv.m_time)
        ++(nr[cdx]);
    }
  }

  size_t k = m_router_list->getSize();
  m_buffer.use(k);
  m_hash_assign = reinterpret_cast<HashAssignElt *>(m_buffer.getTail());

  // If the nr value is 0, then the cache was not included in any
  // last packet, so it should be discarded. A cache is valid if
  // nr is n_routers, indicating that every router mentioned it.
  int v_caches = 0; // valid caches
  for (cdx = 0, cspot = cbegin; cspot != cend; ++cspot, ++cdx)
    if (nr[cdx] == n_routers) {
      m_hash_assign->setAddr(cdx, cspot->idAddr());
      ++v_caches;
    }

  if (!v_caches) { // no valid caches.
    log(LVL_INFO, "Attempted to generate cache assignment but no valid caches were found.");
    return false;
  }
  // Just sets the cache count.
  new (m_hash_assign) HashAssignElt(v_caches);
  m_hash_assign->round_robin_assign();
  m_buffer.use(m_hash_assign->getSize());

  m_mask_assign = reinterpret_cast<MaskAssignElt *>(m_buffer.getTail());
  new (m_mask_assign) MaskAssignElt;

  // For now, hardwire everything to first cache.
  // We have plenty of space, but will need to check if we do something
  // more complex here.
  m_mask_assign->init(0, 0, 0, 0)->addValue(m_hash_assign->getAddr(0), 0, 0, 0, 0);

  logf(LVL_INFO, "Generated assignment for group %d with %d routers, %d valid caches.", group.m_svc.getSvcId(), n_routers,
       v_caches);

  return true;
}
// ------------------------------------------------------
void
BaseMsg::setBuffer(MsgBuffer const &buffer)
{
  m_buffer = buffer;
}

void
BaseMsg::finalize()
{
  m_header.setLength(m_buffer.getCount() - m_header.calcSize());
  m_security.secure(m_buffer);
}

bool
BaseMsg::validateSecurity() const
{
  return m_security.validate(m_buffer);
}
// ------------------------------------------------------
void
HereIAmMsg::fill(detail::cache::GroupData const &group, CacheIdBox const &cache_id, SecurityOption sec_opt)
{
  m_header.fill(m_buffer, HERE_I_AM);
  m_security.fill(m_buffer, sec_opt);
  m_service.fill(m_buffer, group.m_svc);
  m_cache_id.fill(m_buffer, cache_id);
  m_cache_view.fill(m_buffer, group);
}

void
HereIAmMsg::fill_caps(detail::cache::RouterData const &router)
{
  if (router.m_send_caps) {
    m_capabilities.fill(m_buffer, 3);
    m_capabilities.elt(0) = CapabilityElt(CapabilityElt::PACKET_FORWARD_METHOD, router.m_packet_forward);
    m_capabilities.elt(1) = CapabilityElt(CapabilityElt::CACHE_ASSIGNMENT_METHOD, router.m_cache_assign);
    m_capabilities.elt(2) = CapabilityElt(CapabilityElt::PACKET_RETURN_METHOD, router.m_packet_return);
  }
}

int
HereIAmMsg::parse(ts::Buffer const &buffer)
{
  int zret;
  this->setBuffer(buffer);
  if (!m_buffer.getBase())
    return -EINVAL;
  zret = m_header.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;
  if (HERE_I_AM != m_header.getType())
    return PARSE_MSG_WRONG_TYPE;

  // Time to look for components.
  zret = m_security.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  zret = m_service.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  zret = m_cache_id.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  zret = m_cache_view.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  // These are optional so failures can be ignored.
  if (m_buffer.getSpace())
    m_capabilities.parse(m_buffer);
  if (m_buffer.getSpace())
    m_command.parse(m_buffer);

  return m_buffer.getSpace() ? PARSE_DATA_OVERRUN : PARSE_SUCCESS;
}
// ------------------------------------------------------
void
RedirectAssignMsg::fill(detail::cache::GroupData const &group, SecurityOption sec_opt)
{
  m_header.fill(m_buffer, REDIRECT_ASSIGN);
  m_security.fill(m_buffer, sec_opt);
  m_service.fill(m_buffer, group.m_svc);
  switch (group.m_cache_assign) {
  case ServiceGroup::HASH_ONLY:
    m_hash_assign.fill(m_buffer, group.m_assign_info);
    break;
  case ServiceGroup::MASK_ONLY:
    m_alt_mask_assign.fill(m_buffer, group.m_assign_info);
    break;
  default:
    logf(LVL_WARN, "Bad assignment type [%d] for REDIRECT_ASSIGN", group.m_cache_assign);
    break;
  }
}
// ------------------------------------------------------
void
ISeeYouMsg::fill(detail::router::GroupData const &group, SecurityOption sec_opt, detail::Assignment & /* assign ATS_UNUSED */,
                 size_t to_caches, size_t n_routers, size_t n_caches, bool /* send_capabilities ATS_UNUSED */
)
{
  m_header.fill(m_buffer, I_SEE_YOU);
  m_security.fill(m_buffer, sec_opt);
  m_service.fill(m_buffer, group.m_svc);
  m_router_id.fill(m_buffer, to_caches);
  m_router_view.fill(m_buffer, n_routers, n_caches);
}

int
ISeeYouMsg::parse(ts::Buffer const &buffer)
{
  int zret;
  this->setBuffer(buffer);
  if (!m_buffer.getBase())
    return -EINVAL;
  zret = m_header.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;
  if (I_SEE_YOU != m_header.getType())
    return PARSE_MSG_WRONG_TYPE;

  // Time to look for components.
  zret = m_security.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  zret = m_service.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  zret = m_router_id.parse(m_buffer);
  if (PARSE_SUCCESS != zret) {
    logf(LVL_DEBUG, "I_SEE_YOU: Invalid %d router id", zret);
    return zret;
  }

  zret = m_router_view.parse(m_buffer);
  if (PARSE_SUCCESS != zret) {
    logf(LVL_DEBUG, "I_SEE_YOU: Invalid %d router view", zret);
    return zret;
  }

  // Optional components.

  // Test for alternates here
  // At most one of the asssignments but never both.
  // Can be omitted.
  m_assignment.parse(m_buffer);
  m_map.parse(m_buffer);

  // Optional components.
  m_capabilities.parse(m_buffer);
  m_command.parse(m_buffer);

  if (m_buffer.getSpace()) {
    zret = PARSE_DATA_OVERRUN;
    logf(LVL_DEBUG, "I_SEE_YOU: Data overrun %lu", m_buffer.getSpace());
  }

  return zret;
}
// ------------------------------------------------------
int
RemovalQueryMsg::parse(ts::Buffer const &buffer)
{
  int zret;
  this->setBuffer(buffer);
  if (!m_buffer.getBase())
    return -EINVAL;
  zret = m_header.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;
  if (REMOVAL_QUERY != m_header.getType())
    return PARSE_MSG_WRONG_TYPE;

  // Get the components.
  zret = m_security.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  zret = m_service.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  zret = m_query.parse(m_buffer);
  if (PARSE_SUCCESS != zret)
    return zret;

  return m_buffer.getSpace() ? PARSE_DATA_OVERRUN : PARSE_SUCCESS;
}
// ------------------------------------------------------
} // namespace wccp
