    /************************************************* 
  File name     : kpacket.h
  Module        : pal
  Author        : amir.liang   
  Version       : 0.1   
  Created on    : 2021-09-16    
  Description   :
      included files required by os environment
    
  Modify History:   
  1. Date:        Author:         Modification:
     20230424     amir            use os_lock instead of mutex.
*************************************************/

#ifndef __PACKET_H__
#define __PACKET_H__
#include <stdint.h>
#include "hdef.h"
#include "hmutex.h"
#include "hatomic.h"

#ifndef OK
#define OK (char)(0)
#endif
#ifndef NG
#define NG (char)(1)
#endif

#ifdef __cplusplus
extern "C" {   
#endif

/*===========================================*/
/*1. do not use kpacket with stack memory  */
/*2. payload address must be algin 4		 */
/*===========================================*/


#define KPACKET_DEBUG 1U

#if KPACKET_DEBUG
#define KPACKET_LOG hlogd
#else
#define KPACKET_LOG(...)
#endif

#define OS_PACKET_MAGIC  0xcafe

/*
 *kpacket_put_int: 按int推数据到value,  协议方pop_int对应有即可
 *如: kpacket_put_int(kp, 123); val = kpacket_pop_int(kp, -1) val应该是123
*/
typedef struct {
    uint16_t          checksum; /* crc16 , just for TLV, include OS_PACKET_MAGIC */
    uint16_t		  magic;    /* OS_PACKET_MAGIC */
    uint16_t 	      T;        /*TLV  T: 0~65535 */
    uint32_t 		  L;        /*TLV  L: total size of value */
    char			  V[0];     /*TLV  V: the addr of value */
}TLV;

TLV *TLV_init(char *buf, uint32_t cap);


typedef struct{   
    hatomic_t         ref;        /*引用计数,   创建时为1, 当减到值为0时，将释放kpacket内存 */
	uint32_t		  capacity;   /*内存容量,主要是 value能存的数据大小 = sizeof(kpacket) +  len(value) */ 
    uint32_t 	      offset;     /*仅用于标识 value的offset*/
    TLV               tlv;
}kpacket;

    
/*
 * packet_pool_size recommend 4K
 * 初始化时必先 init
*/
void kpacket_init(uint32_t packet_pool_size);


/*
 * 
 * 填充完所有数时，计算并保存checksum到kpacket, 返回checksum
*/
uint16_t kpacket_checksum(kpacket *packet);


/*
 * 
 * 仅计算并返回checksum， 并不保存在kpacket
*/
uint16_t kpacket_calc_checksum(kpacket *packet);


/*
 *
*/
kpacket *kpacket_slab_alloc(uint32_t size);

/*
 *
*/
void kpacket_slab_free(kpacket *packet);

#define kpacket_inc(_packet) ({\
	hatomic_inc(&_packet->ref);/*没锁,有风险*/\
	KPACKET_LOG("%s packet:%p, inc_ref:%d\n", __func__, _packet, _packet->ref);\
})

#define kpacket_dec(_packet) ({\
    if (_packet->ref == 0){\
		hloge("%s Error... hv_free_packet:%p, dec_ref should not be:%d\n", __func__, _packet, _packet->ref);\
	}\
	hatomic_dec(&_packet->ref);/*没锁,可能有风险*/\
	if (_packet->ref == 0){\
		KPACKET_LOG("%s free_packet:%p, dec_ref:%d ..............\n", __func__, _packet, _packet->ref);\
		kpacket_slab_free(_packet);/*os_packet_delete(_packet);*/\
	}else KPACKET_LOG("%s :%p, dec_ref:%d\n", __func__, _packet, _packet->ref);\
})

#define os_packet_valid(__packet) ({int32_t _ret;\
	if (__packet && __packet-->tlv.magic == OS_PACKET_MAGIC){\
		_ret  = 1;\
	}else{\
		KPACKET_LOG("%s err packet:%p(!NULL), packet magic:%X(=%X)\n",__func__, __packet, __packet?__packet-->tlv.magic:0, OS_PACKET_MAGIC);\
		_ret = 0;\
	}\
_ret;})	

#define kpacket(_event, _capacity)({\
    kpacket *_packet = kpacket_slab_alloc(sizeof(kpacket) + _capacity);\
	if (_packet){\
        _packet->tlv.magic    = OS_PACKET_MAGIC;\
        _packet->tlv.T   = _event;\
        _packet->ref      = 1;\
	    _packet->capacity = _capacity;\
	    _packet->tlv.L   = 0;\
	    _packet->offset   = 0;\
	}else{\
		hloge("%s kpacket_create outofmemory", __func__);\
	}\
_packet;})

/*do not call os_packet_delete normally,  use atomic_dec(packet) instead */
#if 0
#define os_packet_delete(_hv_free_packet)({\
	if (os_packet_valid(_hv_free_packet)){\
	    if (_hv_free_packet->ref == 0) {\
	        hv_free(_hv_free_packet);\
			hloge("%s os_packet_hv_free packet:%p",__func__, _hv_free_packet);\
	    }else{\
			hloge("%s os_packet_hv_free error packet->ref:%d",__func__, _hv_free_packet->ref);\
	    }\
	}else{\
		hloge("%s os_packet_hv_free error",__func__);\
	}\
})
#endif

uint32_t kpacket_put_int(kpacket *p, int32_t val);
uint32_t kpacket_put_string(kpacket *p, const char *str);//including '\0'
uint32_t kpacket_put_data(kpacket *p, const char *data, uint32_t len);
uint32_t kpacket_put_data1data2(kpacket *p, const char *data, uint32_t len, const char *data2, uint32_t len2);

//data move to 'zero' and dataLen be 0
void kpacket_reset(kpacket *p);
uint32_t kpacket_get_freesize(const kpacket *p);


uint32_t kpacket_pop_int(kpacket   *p_box, const int32_t defVal);
const char *kpacket_pop_string(kpacket *p_box, const char *defVal);
uint32_t kpacket_pop_data(kpacket    *p_box, char **data);
uint32_t kpacket_pop_reset(kpacket   *p_box);


void kpacket_dump(kpacket *p);

void kpacket_test(void);

#ifdef __cplusplus
}
#endif


#endif /*_OS_TYPE_H__ */

