doubango/tinySIGCOMP/src/tcomp_compartment.c
c732d49e
 #if HAVE_CRT
 #define _CRTDBG_MAP_ALLOC 
 #include <stdlib.h> 
 #include <crtdbg.h>
 #endif //HAVE_CRT
 /*
74ca6d11
 * Copyright (C) 2020, University of the Basque Country (UPV/EHU)
c732d49e
 * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com>
 *
 * The original file was part of Open Source Doubango Framework
 * Copyright (C) 2010-2011 Mamadou Diop.
 * Copyright (C) 2012 Doubango Telecom <http://doubango.org>
 *
 * This file is part of Open Source Doubango Framework.
 *
 * DOUBANGO is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * DOUBANGO 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 DOUBANGO.
 *
 */
 
 
 /**@file tcomp_compartment.c
  * @brief  SigComp compartment.
  * An application-specific grouping of messages that relate to a peer endpoint.  Depending on the signaling protocol, this grouping may
  * relate to application concepts such as "session", "dialog", "connection", or "association".  The application allocates state
  * memory on a per-compartment basis, and determines when a compartment should be created or closed.
  *
  * @author Mamadou Diop <diopmamadou(at)yahoo.fr>
  *
 
  */
 #include "tcomp_compartment.h"
 
 #include "tsk_debug.h"
 
 #include <assert.h>
 
 static void _tcomp_compartment_freeState(tcomp_compartment_t *compartment, tcomp_state_t **lpState);
 
 tcomp_compartment_t* tcomp_compartment_create(uint64_t id, uint32_t sigCompParameters, tsk_bool_t useOnlyACKedStates)
 {
 	tcomp_compartment_t *compartment;
 	if((compartment = tsk_object_new(tcomp_compartment_def_t))){
 		
 
 		/*
 		  +---+---+---+---+---+---+---+---+
 		  |  cpb  |    dms    |    sms    |
 		  +---+---+---+---+---+---+---+---+
 		  |        SigComp_version        |
 		  +---+---+---+---+---+---+---+---+
 		*/
 
 		// I always assume that remote params are equal to local params
 
 		/* Identifier */
 		compartment->identifier = id;
 
 		/* Remote parameters */
 		compartment->remote_parameters = tcomp_params_create();
 		tcomp_params_setParameters(compartment->remote_parameters, sigCompParameters);
 
 		/* Local parameters */
 		compartment->local_parameters = tcomp_params_create();
 		tcomp_params_setParameters(compartment->local_parameters, sigCompParameters);
 
 		/* Total size */
 		compartment->total_memory_size = compartment->total_memory_left = compartment->local_parameters->smsValue;
 
 		/* Empty list. */
 		compartment->nacks = tsk_list_create();
 		
 		/* Empty list. */
 		compartment->local_states = tsk_list_create();
 
 		/* Whether to use only ACKed states */
 		compartment->useOnlyACKedStates = useOnlyACKedStates;
 	}
 	else{
 		TSK_DEBUG_ERROR("Null Compartment");
 	}
 
 	return compartment;
 }
 
 int tcomp_compartment_setUseOnlyACKedStates(tcomp_compartment_t* self, tsk_bool_t useOnlyACKedStates)
 {
 	if(!self){
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return -1;
 	}
 	self->useOnlyACKedStates = useOnlyACKedStates;
 	return 0;
 }
 
 /**Sets remote parameters
 */
 void tcomp_compartment_setRemoteParams(tcomp_compartment_t *compartment, tcomp_params_t *lpParams)
 {
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return;
 	}
 
 	/* CPB||DMS||SMS [1-BYTE] */
 	if(tcomp_params_hasCpbDmsSms(lpParams)){
 		tcomp_params_setCpbCode(compartment->remote_parameters, lpParams->cpbCode);
 		tcomp_params_setDmsCode(compartment->remote_parameters, lpParams->dmsCode);
 		tcomp_params_setSmsCode(compartment->remote_parameters, lpParams->smsCode);
 	}
 
 	/* SigComp version */
 	if(lpParams->SigComp_version){
 		compartment->remote_parameters->SigComp_version = lpParams->SigComp_version;
 	}
 
 	/*
 	*	Returned states
 	*	FIXME: check states about quota
 	*	FIXME: not tic tac
 	*	FIXME: what about returned feedback?
 	*/
 	if(lpParams->returnedStates && tcomp_buffer_getSize(lpParams->returnedStates)){
 		TSK_OBJECT_SAFE_FREE(compartment->remote_parameters->returnedStates);
 		/* swap */
 		compartment->remote_parameters->returnedStates = lpParams->returnedStates;
 		lpParams->returnedStates = 0;
 	}
 }
 
 /**Sets requested feedback
 */
 void tcomp_compartment_setReqFeedback(tcomp_compartment_t *compartment, tcomp_buffer_handle_t *feedback)
 {
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return;
 	}
 
 	tsk_safeobj_lock(compartment);
 
 	/* Delete old */
 	TSK_OBJECT_SAFE_FREE(compartment->lpReqFeedback);
 
 	compartment->lpReqFeedback = tcomp_buffer_create(tcomp_buffer_getBuffer(feedback), tcomp_buffer_getSize(feedback));
 
 	tsk_safeobj_unlock(compartment);
 }
 
 /**Sets returned feedback
 */
 void tcomp_compartment_setRetFeedback(tcomp_compartment_t *compartment, tcomp_buffer_handle_t *feedback)
 {
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return;
 	}
 	
 	tsk_safeobj_lock(compartment);
 	
 	// Delete old
 	TSK_OBJECT_SAFE_FREE(compartment->lpRetFeedback);
 	
 	compartment->lpRetFeedback = tcomp_buffer_create(tcomp_buffer_getBuffer(feedback), tcomp_buffer_getSize(feedback));
 
 	/*
 	* ACK STATE ==> Returned feedback contains the partial state-id.
 	*/
 	if(compartment->compressorData/* && !compartment->compressorData_isStream*/){
 		tcomp_buffer_handle_t *stateid = tcomp_buffer_create(tcomp_buffer_getBufferAtPos(feedback, 1), tcomp_buffer_getSize(feedback)-1);
 		compartment->ackGhost(compartment->compressorData, stateid);
 		TSK_OBJECT_SAFE_FREE(stateid);
 	}
 	
 	tsk_safeobj_unlock(compartment);
 }
 
 /**Clears are compartment from the history.
 */
 void tcomp_compartment_clearStates(tcomp_compartment_t *compartment)
 {
 	if(!compartment){
 		TSK_DEBUG_ERROR("NULL sigcomp compartment.");
 		return;
 	}
 
 	tsk_safeobj_lock(compartment);
 
 	tsk_list_clear_items(compartment->local_states);
 	compartment->total_memory_left = compartment->total_memory_size;
 
 	tsk_safeobj_unlock(compartment);
 }
 
 /**Removes a state from the compartment by priority.
 */
 void tcomp_compartment_freeStateByPriority(tcomp_compartment_t *compartment)
 {
 	tcomp_state_t *lpState;
 	tsk_list_item_t *item;
 
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return;
 	}
 
 	tsk_safeobj_lock(compartment);
 
 	lpState = 0;
 	item = 0;
 
 	/*
 	 * The order in which the existing state items are freed is determined by the state_retention_priority, which is set when the
 	 * state items are created.  The state_retention_priority of 65535 is reserved for locally available states; these states must always be
 	 * freed first.  Apart from this special case, states with the lowest state_retention_priority are always freed first.  In the event of
 	 * a tie, then the state item created first in the compartment is also the first to be freed.
 	*/
 	tsk_list_foreach(item, compartment->local_states){
 		tcomp_state_t *curr = item->data;
 
 		if(!curr){
 			continue;
 		}
 		
 		/* Local state ? */
 		if(curr->retention_priority == 65535){
 			lpState = curr;
 			break;
 		}
 
 		/* Lower priority? */
 		if(!lpState || curr->retention_priority < lpState->retention_priority){
 			lpState = curr;
 			continue;
 		}
 	}
 
 	if(lpState){
 		compartment->total_memory_left += TCOMP_GET_STATE_SIZE(lpState);
 		tsk_list_remove_item_by_data(compartment->local_states, lpState);
 	}
 
 	tsk_safeobj_unlock(compartment);
 }
 
 /**Removes a state from the compartment.
 */
 static void _tcomp_compartment_freeState(tcomp_compartment_t *compartment, tcomp_state_t **lpState)
 {
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return;
 	}
 
 	tsk_safeobj_lock(compartment);
 
 	compartment->total_memory_left += TCOMP_GET_STATE_SIZE(*lpState);
 	tsk_list_remove_item_by_data(compartment->local_states, *lpState);
 	*lpState = tsk_null;
 
 	tsk_safeobj_unlock(compartment);
 }
 
 /**Remove states
 * Called by UDVM after STATE_FREE instruction
 */
 void tcomp_compartment_freeStates(tcomp_compartment_t *compartment, tcomp_tempstate_to_free_t **tempStates, uint8_t size)
 {
 	uint8_t i;
 	tcomp_state_t *lpState;
 	tsk_list_item_t *item;
 
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return;
 	}
 
 	if(!tempStates || !size){
 		return;
 	}
 	
 	lpState = tsk_null;
 	item = tsk_null;
 
 	for (i = 0; i < size; i++){
 		/* lock */
 		tsk_safeobj_lock(compartment);
 
 		tsk_list_foreach(item, compartment->local_states){
 			tcomp_state_t *curr = item->data;
 			
 			/* Compare current state with provided partial state */
 			if(tcomp_buffer_startsWith(curr->identifier, tempStates[i]->identifier)){
 				/*
 				* If more than one state item in the compartment matches the partial state identifier, 
 				* then the state free request is ignored.
 				*/
 				TSK_DEBUG_INFO("Request to free state with usage_count=%d", curr->usage_count);
 				if(tcomp_state_dec_usage_count(curr) == 0){
 					if(lpState){
 						lpState = tsk_null;
 						break;
 					}
 					else{
 						lpState = curr;
 					}
 				}
 			}
 		}
 		
 		/* unlock */
 		tsk_safeobj_unlock(compartment);
 
 		if(lpState){
 			_tcomp_compartment_freeState(compartment, &lpState);
 		}
 	}	
 }
 
 /**Adds a state to the compartment.
 */
 void tcomp_compartment_addState(tcomp_compartment_t *compartment, tcomp_state_t **lpState)
 {
 	tsk_list_item_t *item;
 	int32_t usage_count = 0;
 	const tcomp_buffer_handle_t *identifier;
 	if(!compartment || !lpState || !*lpState){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return;
 	}
 
 	tsk_safeobj_lock(compartment);
 
 	tcomp_state_makeValid(*lpState);
 	identifier = (*lpState)->identifier;
 
 	// check if the state exist
 	tsk_list_foreach(item, compartment->local_states){		
 		if(tcomp_buffer_startsWith(((tcomp_state_t*)item->data)->identifier, (*lpState)->identifier)){
 			*lpState = ((tcomp_state_t*)item->data); // override
 			usage_count = tcomp_state_inc_usage_count(*lpState);
 			break;
 		}
 	}
 
 	if(usage_count == 0){ // alread exist?
 		compartment->total_memory_left -= TCOMP_GET_STATE_SIZE(*lpState);
 		usage_count = tcomp_state_inc_usage_count(*lpState);
 		tsk_list_push_back_data(compartment->local_states, ((void**) lpState));
 	}
 
 	TSK_DEBUG_INFO("SigComp - Add new state with usage_count=%d and id=", usage_count);
 	tcomp_buffer_print(identifier);
 	
 	*lpState = tsk_null;
 
 	tsk_safeobj_unlock(compartment);
 }
 
 /**Finds a state.
 */
 uint32_t tcomp_compartment_findState(tcomp_compartment_t *compartment, const tcomp_buffer_handle_t *partial_identifier, tcomp_state_t **lpState)
 {
 	uint32_t count = 0;
 	tsk_list_item_t *item;
 
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter");
 		return 0;
 	}
 	
 	tsk_safeobj_lock(compartment);
 	
 	tsk_list_foreach(item, compartment->local_states){
 		tcomp_state_t *curr = item->data;
 		
 		if(tcomp_buffer_startsWith(curr->identifier, partial_identifier)){
 			*lpState = curr; // override
 			count++;
 		}
 	}
 
 	tsk_safeobj_unlock(compartment);
 
 	return count;
 }
 
 /**Removes a Ghost state.
 */
 void tcomp_compartment_freeGhostState(tcomp_compartment_t *compartment)
 {
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return;
 	}
 
 	tsk_safeobj_lock(compartment);
 
 	if(compartment->compressorData){
 		compartment->freeGhostState(compartment->compressorData);
 	}
 	else{
 		TSK_DEBUG_WARN("No compression data to free.");
 	}
 
 	tsk_safeobj_unlock(compartment);
 }
 
 /**Adds a NACK to the compartment.
 */
 void tcomp_compartment_addNack(tcomp_compartment_t *compartment, const uint8_t nackId[TSK_SHA1_DIGEST_SIZE])
 {
 	tcomp_buffer_handle_t *id;
 
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return;
 	}
 
 #if 0
 	{
 		int i;
 		TSK_DEBUG_INFO("Save NACK with id:");
 		for(i = 0; i < TSK_SHA1_DIGEST_SIZE; ++i){
 			printf("%x ", nackId[i]);
 		}
 		printf("\n");
 	}
 #endif
 
 	tsk_safeobj_lock(compartment);
 
 	if(compartment->nacks_history_count >= NACK_MAX_HISTORY_SIZE){
 		tsk_list_remove_last_item(compartment->nacks);
 		compartment->nacks_history_count--;
 	}
 
 	id = tcomp_buffer_create(nackId, TSK_SHA1_DIGEST_SIZE);
 	tsk_list_push_back_data(compartment->nacks, ((void**) &id));
 	compartment->nacks_history_count++;
 
 	tsk_safeobj_unlock(compartment);
 }
 
 /**Checks if the NACK exist.
 */
 tsk_bool_t tcomp_compartment_hasNack(tcomp_compartment_t *compartment, const tcomp_buffer_handle_t *nackId)
 {
 	tsk_bool_t ret = tsk_false;
 	tsk_list_item_t *item;
 
 	if(!compartment){
 		TSK_DEBUG_ERROR("Invalid parameter.");
 		return tsk_false;
 	}
 
 	tsk_safeobj_lock(compartment);
 
 	item = tsk_null;
 	
 	tsk_list_foreach(item, compartment->nacks){
 		tcomp_buffer_handle_t *curr = item->data;
 	
 		if(tcomp_buffer_equals(curr, nackId)){
 			TSK_DEBUG_INFO("SigComp - Nack found.");
 			ret = tsk_true;
 			break;
 		}
 	}
 
 	tsk_safeobj_unlock(compartment);
 
 	return ret;
 }
 
 
 
 
 
 
 
 
 
 
 //========================================================
 //	State object definition
 //
 
 static tsk_object_t* tcomp_compartment_ctor(tsk_object_t* self, va_list * app)
 {
 	tcomp_compartment_t *compartment = self;
 	if(compartment){
 		/* Initialize safeobject */
 		tsk_safeobj_init(compartment);
 	}
 
 	return self;
 }
 
 static tsk_object_t* tcomp_compartment_dtor(tsk_object_t* self)
 {
 	tcomp_compartment_t *compartment = self;
 	if(compartment){
 		/* Deinitialize safeobject */
 		tsk_safeobj_deinit(compartment);
 		
 		/* Delete feedbacks */
 		TSK_OBJECT_SAFE_FREE(compartment->lpReqFeedback);
 		TSK_OBJECT_SAFE_FREE(compartment->lpRetFeedback);
 		
 		/* Delete Nacks */
 		TSK_OBJECT_SAFE_FREE(compartment->nacks);
 		
 		/* Delete Compressor data */
 		TSK_OBJECT_SAFE_FREE(compartment->compressorData);
 		compartment->ackGhost = tsk_null;
 		compartment->freeGhostState = tsk_null;
 		
 		/* Delete params */
 		TSK_OBJECT_SAFE_FREE(compartment->local_parameters);
 		TSK_OBJECT_SAFE_FREE(compartment->remote_parameters);
 
 		/* Delete NACKS */
 		TSK_OBJECT_SAFE_FREE(compartment->nacks);
 
 		/* Delete local states */
 		TSK_OBJECT_SAFE_FREE(compartment->local_states);
 	}
 	else{
 		TSK_DEBUG_ERROR("Null Compartment");
 	}
 	
 	return self;
 }
 
 static int tcomp_compartment_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2)
 {
 	const tcomp_compartment_t *compartment1 = obj1;
 	const tcomp_compartment_t *compartment2 = obj2;
 	int64_t res = (compartment1->identifier - compartment2->identifier);
 	return res > 0 ? (int)1 : (res < 0 ? (int)-1 : (int)0);
 }
 
 static const tsk_object_def_t tsk_compartment_def_s = 
 {
 	sizeof(tcomp_compartment_t),
 	tcomp_compartment_ctor,
 	tcomp_compartment_dtor,
 	tcomp_compartment_cmp
 };
 const tsk_object_def_t *tcomp_compartment_def_t = &tsk_compartment_def_s;