Index: uspace/lib/socket/packet/packet.c
===================================================================
--- uspace/lib/socket/packet/packet.c	(revision 849ed54afbef3ad0ec3af831e93a1353f9eaaf0f)
+++ uspace/lib/socket/packet/packet.c	(revision 849ed54afbef3ad0ec3af831e93a1353f9eaaf0f)
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2009 Lukas Mejdrech
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup packet
+ *  @{
+ */
+
+/** @file
+ *  Packet map and queue implementation.
+ *  This file has to be compiled with both the packet server and the client.
+ */
+
+#include <errno.h>
+#include <malloc.h>
+#include <mem.h>
+#include <fibril_synch.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+
+#include <net_err.h>
+#include <adt/generic_field.h>
+#include <packet/packet.h>
+#include <packet/packet_header.h>
+
+/** Packet map page size.
+ */
+#define PACKET_MAP_SIZE	100
+
+/** Returns the packet map page index.
+ *  @param[in] packet_id The packet identifier.
+ */
+#define PACKET_MAP_PAGE(packet_id)	(((packet_id) - 1) / PACKET_MAP_SIZE)
+
+/** Returns the packet index in the corresponding packet map page.
+ *  @param[in] packet_id The packet identifier.
+ */
+#define PACKET_MAP_INDEX(packet_id)	(((packet_id) - 1) % PACKET_MAP_SIZE)
+
+/** Type definition of the packet map page.
+ */
+typedef packet_t packet_map_t[PACKET_MAP_SIZE];
+/** Type definition of the packet map page pointer.
+ */
+typedef packet_map_t * packet_map_ref;
+
+/** Packet map.
+ *  Maps packet identifiers to the packet references.
+ *  @see generic_field.h
+ */
+GENERIC_FIELD_DECLARE(gpm, packet_map_t);
+
+/** Releases the packet.
+ *  @param[in] packet The packet to be released.
+ *  @returns EOK on success.
+ *  @returns EINVAL if the packet is not valid.
+ */
+int packet_destroy(packet_t packet);
+
+/** Packet map global data.
+ */
+static struct{
+	/** Safety lock.
+	 */
+	fibril_rwlock_t lock;
+	/** Packet map.
+	 */
+	gpm_t packet_map;
+} pm_globals;
+
+GENERIC_FIELD_IMPLEMENT(gpm, packet_map_t);
+
+int packet_destroy(packet_t packet){
+	if(! packet_is_valid(packet)){
+		return EINVAL;
+	}
+	return munmap(packet, packet->length);
+}
+
+int pm_init(void){
+	ERROR_DECLARE;
+
+	fibril_rwlock_initialize(&pm_globals.lock);
+	fibril_rwlock_write_lock(&pm_globals.lock);
+	ERROR_PROPAGATE(gpm_initialize(&pm_globals.packet_map));
+	fibril_rwlock_write_unlock(&pm_globals.lock);
+	return EOK;
+}
+
+packet_t pm_find(packet_id_t packet_id){
+	packet_map_ref map;
+	packet_t packet;
+
+	if(! packet_id){
+		return NULL;
+	}
+	fibril_rwlock_read_lock(&pm_globals.lock);
+	if(packet_id > PACKET_MAP_SIZE * gpm_count(&pm_globals.packet_map)){
+		fibril_rwlock_read_unlock(&pm_globals.lock);
+		return NULL;
+	}
+	map = gpm_get_index(&pm_globals.packet_map, PACKET_MAP_PAGE(packet_id));
+	if(! map){
+		fibril_rwlock_read_unlock(&pm_globals.lock);
+		return NULL;
+	}
+	packet = (*map)[PACKET_MAP_INDEX(packet_id)];
+	fibril_rwlock_read_unlock(&pm_globals.lock);
+	return packet;
+}
+
+int pm_add(packet_t packet){
+	ERROR_DECLARE;
+
+	packet_map_ref map;
+
+	if(! packet_is_valid(packet)){
+		return EINVAL;
+	}
+	fibril_rwlock_write_lock(&pm_globals.lock);
+	if(PACKET_MAP_PAGE(packet->packet_id) < gpm_count(&pm_globals.packet_map)){
+		map = gpm_get_index(&pm_globals.packet_map, PACKET_MAP_PAGE(packet->packet_id));
+	}else{
+		do{
+			map = (packet_map_ref) malloc(sizeof(packet_map_t));
+			if(! map){
+				fibril_rwlock_write_unlock(&pm_globals.lock);
+				return ENOMEM;
+			}
+			bzero(map, sizeof(packet_map_t));
+			if((ERROR_CODE = gpm_add(&pm_globals.packet_map, map)) < 0){
+				fibril_rwlock_write_unlock(&pm_globals.lock);
+				free(map);
+				return ERROR_CODE;
+			}
+		}while(PACKET_MAP_PAGE(packet->packet_id) >= gpm_count(&pm_globals.packet_map));
+	}
+	(*map)[PACKET_MAP_INDEX(packet->packet_id)] = packet;
+	fibril_rwlock_write_unlock(&pm_globals.lock);
+	return EOK;
+}
+
+void pm_destroy(void){
+	int count;
+	int index;
+	packet_map_ref map;
+	packet_t packet;
+
+	fibril_rwlock_write_lock(&pm_globals.lock);
+	count = gpm_count(&pm_globals.packet_map);
+	while(count > 0){
+		map = gpm_get_index(&pm_globals.packet_map, count - 1);
+		for(index = PACKET_MAP_SIZE - 1; index >= 0; -- index){
+			packet = (*map)[index];
+			if(packet_is_valid(packet)){
+				munmap(packet, packet->length);
+			}
+		}
+	}
+	gpm_destroy(&pm_globals.packet_map);
+	// leave locked
+}
+
+int pq_add(packet_t * first, packet_t packet, size_t order, size_t metric){
+	packet_t item;
+
+	if((! first) || (! packet_is_valid(packet))){
+		return EINVAL;
+	}
+	pq_set_order(packet, order, metric);
+	if(packet_is_valid(*first)){
+		item = * first;
+		do{
+			if(item->order < order){
+				if(item->next){
+					item = pm_find(item->next);
+				}else{
+					item->next = packet->packet_id;
+					packet->previous = item->packet_id;
+					return EOK;
+				}
+			}else{
+				packet->previous = item->previous;
+				packet->next = item->packet_id;
+				item->previous = packet->packet_id;
+				item = pm_find(packet->previous);
+				if(item){
+					item->next = packet->packet_id;
+				}else{
+					*first = packet;
+				}
+				return EOK;
+			}
+		}while(packet_is_valid(item));
+	}
+	*first = packet;
+	return EOK;
+}
+
+packet_t pq_find(packet_t packet, size_t order){
+	packet_t item;
+
+	if(! packet_is_valid(packet)){
+		return NULL;
+	}
+	item = packet;
+	do{
+		if(item->order == order){
+			return item;
+		}
+		item = pm_find(item->next);
+	}while(item && (item != packet) && packet_is_valid(item));
+	return NULL;
+}
+
+int pq_insert_after(packet_t packet, packet_t new_packet){
+	packet_t item;
+
+	if(!(packet_is_valid(packet) && packet_is_valid(new_packet))){
+		return EINVAL;
+	}
+	new_packet->previous = packet->packet_id;
+	new_packet->next = packet->next;
+	item = pm_find(packet->next);
+	if(item){
+		item->previous = new_packet->packet_id;
+	}
+	packet->next = new_packet->packet_id;
+	return EOK;
+}
+
+packet_t pq_detach(packet_t packet){
+	packet_t next;
+	packet_t previous;
+
+	if(! packet_is_valid(packet)){
+		return NULL;
+	}
+	next = pm_find(packet->next);
+	if(next){
+		next->previous = packet->previous;
+		previous = pm_find(next->previous);
+		if(previous){
+			previous->next = next->packet_id;
+		}
+	}
+	packet->previous = 0;
+	packet->next = 0;
+	return next;
+}
+
+int pq_set_order(packet_t packet, size_t order, size_t metric){
+	if(! packet_is_valid(packet)){
+		return EINVAL;
+	}
+	packet->order = order;
+	packet->metric = metric;
+	return EOK;
+}
+
+int pq_get_order(packet_t packet, size_t * order, size_t * metric){
+	if(! packet_is_valid(packet)){
+		return EINVAL;
+	}
+	if(order){
+		*order = packet->order;
+	}
+	if(metric){
+		*metric = packet->metric;
+	}
+	return EOK;
+}
+
+void pq_destroy(packet_t first, void (*packet_release)(packet_t packet)){
+	packet_t actual;
+	packet_t next;
+
+	actual = first;
+	while(packet_is_valid(actual)){
+		next = pm_find(actual->next);
+		actual->next = 0;
+		actual->previous = 0;
+		if(packet_release){
+			packet_release(actual);
+		}
+		actual = next;
+	}
+}
+
+packet_t pq_next(packet_t packet){
+	if(! packet_is_valid(packet)){
+		return NULL;
+	}
+	return pm_find(packet->next);
+}
+
+packet_t pq_previous(packet_t packet){
+	if(! packet_is_valid(packet)){
+		return NULL;
+	}
+	return pm_find(packet->previous);
+}
+
+/** @}
+ */
