/*
 * Copyright(C) 2009 Проект ИОК
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто-Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто-Про.
 */

/*!
 * \file $RCSfile$
 * \version $Revision: 210017 $
 * \date $Date:: 2020-04-15 15:48:53 +0300#$
 * \author $Author: dmax $
 *
 * \brief Итераторы для CSP_iovec
 */

#ifndef CSP_iovec_iterator_h_
#define CSP_iovec_iterator_h_
#include "reader/support.h"
#include "WinCryptEx.h"
#include "compiler_attributes.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifndef _len_of
    #define _len_of(a)    (sizeof(a)/sizeof(*(a)))
#endif

    // Получение CSP_iovec из pbData и dwFlags
static SUP_INLINE ATTR_USERES CSP_iovec *
Ciov(void *pbData, DWORD dwFlags){
    if(dwFlags&CP_CRYPT_DATA_IOVEC) {
	return (CSP_iovec *)pbData;
    }
    return NULL;
}

    // Получение CSP_iovec из pbData и dwFlags
static SUP_INLINE ATTR_USERES const CSP_iovec *
CCiov(const void *pbData, DWORD dwFlags){
    if(dwFlags&CP_CRYPT_DATA_IOVEC) {
	return (const CSP_iovec *)pbData;
    }
    return NULL;
}

static  SUP_INLINE ATTR_USERES int
Ciov_check(const CSP_iovec *piov){
    if(CSP_UIOV_MAXBAD_LEN <= piov->CSPiov_len) {
	return 0;
    }
    if(NULL == piov->CSPiov_ptr) {
	if(0 != piov->CSPiov_len) {
	    return 0;
	}
    }
    return 1;
}

    // Присваивание полям CSP_iovec
static SUP_INLINE ATTR_USERES CSPiov_ptr_type
Ciov_ptr(void *pv){
    return (CSPiov_ptr_type)pv;
}

static SUP_INLINE ATTR_USERES CSPiov_len_type
Ciov_dw2len(DWORD dw){
    if(dw >= CSP_UIOV_MAXBAD_LEN) {
	return CSP_UIOV_MAXBAD_LEN;
    }
    return (CSPiov_len_type)dw;
}

static SUP_INLINE ATTR_USERES CSPiov_len_type
Ciov_s2len(size_t s){
    if(s >= CSP_UIOV_MAXBAD_LEN) {
	return CSP_UIOV_MAXBAD_LEN;
    }
    return (CSPiov_len_type)s;
}

    // Итератор
typedef struct CSP_iovec_iterator_ {
    CSP_iovec	    *ciov;
    CSPiov_len_type  off;
} CSP_iovec_iterator;

    // Присваивание итератору
static SUP_INLINE void
Ciov_set(CSP_iovec_iterator *i, CSP_iovec *p)
{
    i->ciov = p;
    i->off = 0;
}

static SUP_INLINE void
Ciov_setEOB(CSP_iovec_iterator *i, CSP_iovec *p)
{
    i->ciov = p;
    i->off = p->CSPiov_len;
}

    // Сравнение итераторов
static SUP_INLINE ATTR_USERES int
Ciov_cmp(const CSP_iovec_iterator *a, const CSP_iovec_iterator *b)
{
    if(a->ciov+1 < b->ciov) {
	return -1;
    } else if(a->ciov == b->ciov) {
	if(a->off < b->off) {
	    return -1;
	}else if(a->off == b->off) {
	    return 0;
	}
	/* a->off < b->off */
	return 1;
    } else if(a->ciov > b->ciov+1) {
	return 1;
    } else if(a->ciov+1 == b->ciov) {
	if(a->off < a->ciov->CSPiov_len ||
	   b->off > 0) {
	    return -1;
	}
	return 0;
    }
    /*a->ciov == b->ciov+1*/
    if(a->off > 0 ||
       b->off < b->ciov->CSPiov_len) {
	return 1;
    }
    return 0;
}

    // Продвижение итераторов
// CPCSP-6940: 2020-feb-04: dim, pav стоит подумать о проверке кода возврата в KChanSck.c
// непонятно, к чему может привести, а именно сейчас не время для экспериментов над HSM
static SUP_INLINE CSPiov_len_type
Ciov_advance(CSP_iovec_iterator *i, CSPiov_len_type a, 
	     CSP_iovec_iterator *last)
{
    CSPiov_len_type s = 0;
    CSPiov_len_type o = a;

    if(last->ciov->CSPiov_len < last->off) {
	return CSP_UIOV_MAXBAD_LEN;
    }
    while(Ciov_cmp(i, last) < 0) {
	if(i->ciov->CSPiov_len < i->off) {
	    return CSP_UIOV_MAXBAD_LEN;
	}
	if(o > i->ciov->CSPiov_len - i->off && i->ciov < last->ciov) {
	    s += i->ciov->CSPiov_len - i->off;
	    o -= i->ciov->CSPiov_len - i->off;
	    i->ciov++;
	    i->off = 0;
	} else if(i->ciov == last->ciov) {
	    if(last->off < i->off) {
		return s;
	    }
	    if(i->off + o > last->off) {
		o = last->off - i->off;
	    }
	    s += o;
	    i->off += o;
	    break;
	} else {
	    s += o;
	    i->off += o;
	    break;
	}
    }
    if(0 != s && 0 == i->off) {
	i->ciov--;
	i->off = i->ciov->CSPiov_len;
    }
    return s;
}

static SUP_INLINE ATTR_USERES CSPiov_len_type
Ciov_backward(CSP_iovec_iterator *i, CSPiov_len_type d, 
	     CSP_iovec_iterator *first)
{
    CSPiov_len_type s = 0;
    CSPiov_len_type o = d;

    if(first->ciov->CSPiov_len < first->off) {
	return CSP_UIOV_MAXBAD_LEN;
    }
    while(Ciov_cmp(i, first) > 0) {
	if(i->ciov->CSPiov_len < i->off) {
	    return CSP_UIOV_MAXBAD_LEN;
	}
	if(o > i->off && i->ciov > first->ciov) {
	    s += i->off;
	    o -= i->off;
	    i->ciov--;
	    i->off = i->ciov->CSPiov_len;
	} else if(i->ciov == first->ciov) {
	    if(i->off < first->off) {
		return s;
	    }
	    if(first->off + o > i->off) {
		o = i->off - first->off;
	    }
	    s += o;
	    i->off -= o;
	    break;
	} else {
	    s += o;
	    i->off -= o;
	    break;
	}
    }
    return s;
}

static SUP_INLINE ATTR_USERES int
Ciov_diff_cnt(const CSP_iovec_iterator *minuend, 
	      const CSP_iovec_iterator *subtrahend)
{
    if(minuend->off) {
	return (int)(minuend->ciov - subtrahend->ciov + 1);
    }
    return (int)(minuend->ciov - subtrahend->ciov);
}

static SUP_INLINE ATTR_USERES CSPiov_len_type
Ciov_diff_len(const CSP_iovec_iterator *minuend, 
	      const CSP_iovec_iterator *subtrahend)
{
    CSPiov_len_type clLen = 0;
    const CSP_iovec *p;

    for(p = subtrahend->ciov; p <= minuend->ciov; p++) {
	if(!Ciov_check(p)) {
	    return CSP_UIOV_MAXBAD_LEN;
	}
	clLen += p->CSPiov_len;
    }
    clLen -= subtrahend->off + (minuend->ciov->CSPiov_len - minuend->off);
    return clLen;
}

#define Ciov_prepare(a, b) {					\
	    CSP_iovec *_Ciov_a = (a)->ciov;			\
	    CSP_iovec *_Ciov_b = (b)->ciov;			\
	    CSPiov_len_type _Ciov_off_a = (a)->off;		\
	    CSPiov_len_type _Ciov_tail_b =			\
	    			_Ciov_b->CSPiov_len - (b)->off;	\
	    char *tp = (char*) _Ciov_a->CSPiov_ptr;             \
	    tp += _Ciov_off_a;                                  \
	    _Ciov_a->CSPiov_ptr = (CSPiov_ptr_type) tp;         \
	    _Ciov_a->CSPiov_len -= _Ciov_off_a;			\
	    _Ciov_b->CSPiov_len -= _Ciov_tail_b;		\
	    {
#define Ciov_revert()						\
	    }							\
	    _Ciov_b->CSPiov_len += _Ciov_tail_b;		\
	    _Ciov_a->CSPiov_len += _Ciov_off_a;			\
	    tp = (char*) _Ciov_a->CSPiov_ptr;                   \
	    tp -= _Ciov_off_a;                                  \
	    _Ciov_a->CSPiov_ptr = (CSPiov_ptr_type) tp;         \
	    }

#ifdef __cplusplus
}
#endif
#endif /* CSP_iovec_iterator_h_ */
/* $Id: CSP_iovec_iterator.h 210017 2020-04-15 12:48:53Z dmax $ */
