
/*
 * msg.hh
 * Copyright (C) 2002 by John Heidemann <johnh@isi.edu>
 * $Id: msg.cc,v 1.9 2002/04/05 21:36:56 johnh Exp $
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 *
 */

/*
 * msg.cc:
 * deals with LSA message marshalling and unmarshalling.
 */

#include <assert.h>

#include "msg.hh"
#include "main.hh"

/*
 * marshal_one() encodes one data unit (8, 16, or 32-bit value).
 */
void
LSA::marshal_one(int n, int size)
{
	// Pack and convert to network byte order
	// (not required for proj a, but worth doing).
	switch(size) {
	case 1:
		bytes_[bytes_marshalled_++] = ((n>>0) & 0xff);
		break;
	case 2:
		bytes_[bytes_marshalled_++] = ((n>>8) & 0xff);
		bytes_[bytes_marshalled_++] = ((n>>0) & 0xff);
		break;
	case 4:
		bytes_[bytes_marshalled_++] = ((n>>24) & 0xff);
		bytes_[bytes_marshalled_++] = ((n>>16) & 0xff);
		bytes_[bytes_marshalled_++] = ((n>>8) & 0xff);
		bytes_[bytes_marshalled_++] = ((n>>0) & 0xff);
		break;
	default:
		die("unknown size\n");
		break;
	};
	assert(bytes_marshalled_ < MAX_BYTES);
}

int
LSA::unmarshal_one(int size)
{
	int n = 0;
	// unpack and convert from network byte order
	switch(size) {
	case 1:
		n = (n << 8) + bytes_[bytes_unmarshalled_++];
		break;
	case 2:
		n = (n << 8) + bytes_[bytes_unmarshalled_++];
		n = (n << 8) + bytes_[bytes_unmarshalled_++];
		break;
	case 4:
		n = (n << 8) + bytes_[bytes_unmarshalled_++];
		n = (n << 8) + bytes_[bytes_unmarshalled_++];
		n = (n << 8) + bytes_[bytes_unmarshalled_++];
		n = (n << 8) + bytes_[bytes_unmarshalled_++];
		break;
	default:
		die("unknown size\n");
		break;
	};
	assert(bytes_unmarshalled_ <= bytes_marshalled_);

	return n;
}

void
LSA::marshal()
{
	// go from data structure to bytes
	/*
	 * Note that the data structure sizes are hardcoded here.
	 * That's ok because the spec forces their sizes.
	 * We don't want to use sizeof() because that may not
	 * match the spec.
	 */
	bytes_marshalled_ = 0;
	marshal_one(ack_, 1);
	marshal_one(seqno_, 2);
	marshal_one(ttl_, 2);
	marshal_one(origin_id_, 2);
	marshal_one(last_id_, 2);
	marshal_one(num_routes_, 2);
	marshal_one(num_links_, 2);
	for (list<Route*>::iterator route_it = routes_.begin();
	     route_it != routes_.end();
	     route_it++) {
		marshal_one((*route_it)->addr(), 4);
		marshal_one((*route_it)->prefix(), 1);
		marshal_one((*route_it)->med(), 2);
	};
	for (list<int>::iterator link_it = links_.begin();
	     link_it != links_.end();
	     link_it++) {
		marshal_one((*link_it), 2);
	};
}

void
LSA::unmarshal()
{
	// go from bytes to data structure
	bytes_unmarshalled_ = 0;
	ack_ = unmarshal_one(1);
	seqno_ = unmarshal_one(2);
	ttl_ = unmarshal_one(2);
	origin_id_ = unmarshal_one(2);
	last_id_ = unmarshal_one(2);
	num_routes_ = unmarshal_one(2);
	num_links_ = unmarshal_one(2);
	for (int i = 0; i < num_routes_; i++) {
		int addr = unmarshal_one(4);
		int prefix = unmarshal_one(1);
		int med = unmarshal_one(2);
		Route *rte = new Route(new AddrPrefix(addr, prefix),
				       origin_id_,
				       0, 0, med);
		routes_.push_back(rte);
	};
	for (int i = 0; i < num_links_; i++) {
		int id = unmarshal_one(2);
		links_.push_back(id);
	};
}

LSA::~LSA()
{
	assert(refs_ <= 0);
	while (!routes_.empty()) {
		routes_.front()->deref();
		routes_.pop_front();
	};
}

LSA *
LSA::clone()
{
	LSA *lsa = new LSA;
	lsa->ack_ = ack_;
	lsa->seqno_ = seqno_;
	lsa->ttl_ = ttl_;
	lsa->origin_id_ = origin_id_;
	lsa->last_id_ = last_id_;
	// xxx: these copies are NOT good, idomatic STL  Sigh
	lsa->num_routes_ = num_routes_;
	for (list<Route*>::iterator r_it = routes_.begin();
	     r_it != routes_.end();
	     r_it++) {
		(*r_it)->ref();
		lsa->routes_.push_back(*r_it);
	};
	lsa->num_links_ = num_links_;
	for (list<int>::iterator l_it = links_.begin();
	     l_it != links_.end();
	     l_it++) {
		lsa->links_.push_back(*l_it);
	};

	lsa->marshal();

	return lsa;
}
