text: /*-
   * Copyright (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
   * Redistribution and modifications are permitted subject to BSD license.
   */
  #include "asn_internal.h"
  #include "ber_tlv_length.h"
  #include "ber_tlv_tag.h"
  
  ssize_t
  ber_fetch_length(int _is_constructed, const void *bufptr, size_t size,
  		ber_tlv_len_t *len_r) {
  	const uint8_t *buf = (const uint8_t *)bufptr;
  	unsigned oct;
  
  	if(size == 0)
  		return 0;	/* Want more */
  
  	oct = *(const uint8_t *)buf;
  	if((oct & 0x80) == 0) {
  		/*
  		 * Short definite length.
  		 */
  		*len_r = oct;	/* & 0x7F */
  		return 1;
  	} else {
  		ber_tlv_len_t len;
  		size_t skipped;
  
  		if(_is_constructed && oct == 0x80) {
  			*len_r = -1;	/* Indefinite length */
  			return 1;
  		}
  
  		if(oct == 0xff) {
  			/* Reserved in standard for future use. */
  			return -1;
  		}
  
  		oct &= 0x7F;	/* Leave only the 7 LS bits */
  		for(len = 0, buf++, skipped = 1;
  			oct && (++skipped <= size); buf++, oct--) {
  
  			/* Verify that we won't overflow. */
  			if(!(len >> ((8 * sizeof(len)) - (8+1)))) {
  				len = (len << 8) | *buf;
  			} else {
  				/* Too large length value. */
  				return -1;
  			}
  		}
  
  		if(oct == 0) {
  			if(len < 0 || len > RSSIZE_MAX) {
  				/* Length value out of sane range. */
  				return -1;
  			}
  
  			*len_r = len;
  			return skipped;
  		}
  
  		return 0;	/* Want more */
  	}
  
  }
  
  ssize_t
  ber_skip_length(const asn_codec_ctx_t *opt_codec_ctx,
  		int _is_constructed, const void *ptr, size_t size) {
  	ber_tlv_len_t vlen;	/* Length of V in TLV */
  	ssize_t tl;		/* Length of L in TLV */
  	ssize_t ll;		/* Length of L in TLV */
  	size_t skip;
  
  	/*
  	 * Make sure we didn't exceed the maximum stack size.
  	 */
  	if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx))
  		return -1;
  
  	/*
  	 * Determine the size of L in TLV.
  	 */
  	ll = ber_fetch_length(_is_constructed, ptr, size, &vlen);
  	if(ll <= 0) return ll;
  
  	/*
  	 * Definite length.
  	 */
  	if(vlen >= 0) {
  		skip = ll + vlen;
  		if(skip > size)
  			return 0;	/* Want more */
  		return skip;
  	}
  
  	/*
  	 * Indefinite length!
  	 */
  	ASN_DEBUG("Skipping indefinite length");
  	for(skip = ll, ptr = ((const char *)ptr) + ll, size -= ll;;) {
  		ber_tlv_tag_t tag;
  
  		/* Fetch the tag */
  		tl = ber_fetch_tag(ptr, size, &tag);
  		if(tl <= 0) return tl;
  
  		ll = ber_skip_length(opt_codec_ctx,
  			BER_TLV_CONSTRUCTED(ptr),
  			((const char *)ptr) + tl, size - tl);
  		if(ll <= 0) return ll;
  
  		skip += tl + ll;
  
  		/*
  		 * This may be the end of the indefinite length structure,
  		 * two consecutive 0 octets.
  		 * Check if it is true.
  		 */
  		if(((const uint8_t *)ptr)[0] == 0
  		&& ((const uint8_t *)ptr)[1] == 0)
  			return skip;
  
  		ptr = ((const char *)ptr) + tl + ll;
  		size -= tl + ll;
   	}
  
  	/* UNREACHABLE */
  }
  
  size_t
  der_tlv_length_serialize(ber_tlv_len_t len, void *bufp, size_t size) {
  	size_t required_size;	/* Size of len encoding */
  	uint8_t *buf = (uint8_t *)bufp;
  	uint8_t *end;
  	int i;
  
  	if(len <= 127) {
  		/* Encoded in 1 octet */
  		if(size) *buf = (uint8_t)len;
  		return 1;
  	}
  
  	/*
  	 * Compute the size of the subsequent bytes.
  	 */
  	for(required_size = 1, i = 8; i < 8 * (int)sizeof(len); i += 8) {
  		if(len >> i)
  			required_size++;
  		else
  			break;
  	}
  
  	if(size <= required_size)
  		return required_size + 1;
  
  	*buf++ = (uint8_t)(0x80 | required_size);  /* Length of the encoding */
  
  	/*
  	 * Produce the len encoding, space permitting.
  	 */
  	end = buf + required_size;
  	for(i -= 8; buf < end; i -= 8, buf++)
  		*buf = (uint8_t)(len >> i);
  
  	return required_size + 1;
  }
  
  