in lib/asn1/c_src/asn1_erl_nif.c [65:491]
static int per_insert_octets_except_unused(int, unsigned char **, unsigned char **,
int *, int);
static int per_insert_octets_as_bits_exact_len(int, int, unsigned char **,
unsigned char **, int *);
static int per_insert_octets_as_bits(int, unsigned char **, unsigned char **, int *);
static int per_pad_bits(int, unsigned char **, int *);
static int per_insert_least_sign_bits(int, unsigned char, unsigned char **, int *);
static int per_insert_most_sign_bits(int, unsigned char, unsigned char **, int *);
static int per_insert_bits_as_bits(int, int, unsigned char **, unsigned char **, int *);
static int per_insert_octets_unaligned(int, unsigned char **, unsigned char **, int);
static int per_realloc_memory(ErlNifBinary *, int, unsigned char **);
/* BER DECODE */
static int ber_decode_begin(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int,
unsigned int *);
static int ber_decode(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int *, int);
static int ber_decode_tag(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int, int *);
static int ber_decode_value(ErlNifEnv*, ERL_NIF_TERM *, unsigned char *, int *, int,
int);
/* BER ENCODE */
typedef struct ber_encode_mem_chunk mem_chunk_t;
static int ber_encode(ErlNifEnv *, ERL_NIF_TERM , mem_chunk_t **, unsigned int *);
static void ber_free_chunks(mem_chunk_t *chunk);
static mem_chunk_t *ber_new_chunk(unsigned int length);
static int ber_check_memory(mem_chunk_t **curr, unsigned int needed);
static int ber_encode_tag(ErlNifEnv *, ERL_NIF_TERM , unsigned int ,
mem_chunk_t **, unsigned int *);
static int ber_encode_length(size_t , mem_chunk_t **, unsigned int *);
/*
*
* This section defines functionality for the complete encode of a
* PER encoded message
*
*/
static int per_complete(ErlNifBinary *out_binary, unsigned char *in_buf,
int in_buf_len) {
int counter = in_buf_len;
/* counter keeps track of number of bytes left in the
input buffer */
int buf_space = in_buf_len;
/* This is the amount of allocated space left of the out_binary. It
is possible when padding is applied that more space is needed than
was originally allocated. */
int buf_size = in_buf_len;
/* Size of the buffer. May become reallocated and thus other than
in_buf_len */
unsigned char *in_ptr, *ptr;
/* in_ptr points at the next byte in in_buf to be moved to
complete_buf.
ptr points into the new completed buffer, complete_buf, at the
position of the next byte that will be set */
int unused = 8;
/* unused = [1,...,8] indicates how many of the rigthmost bits of
the byte that ptr points at that are unassigned */
int no_bits, no_bytes, in_unused, desired_len, ret, saved_mem, needed,
pad_bits;
unsigned char val;
in_ptr = in_buf;
ptr = out_binary->data;
*ptr = 0x00;
while (counter > 0) {
counter--;
switch (*in_ptr) {
case 0:
/* just one zero-bit should be added to the buffer */
if (unused == 1) {
unused = 8;
*++ptr = 0x00;
buf_space--;
} else
unused--;
break;
case 1:
/* one one-bit should be added to the buffer */
if (unused == 1) {
*ptr = *ptr | 1;
unused = 8;
*++ptr = 0x00;
buf_space--;
} else {
*ptr = *ptr | (1 << (unused - 1));
unused--;
}
break;
case 2:
/* align buffer to end of byte */
if (unused != 8) {
*++ptr = 0x00;
buf_space--;
unused = 8;
}
break;
case 10:
/* next byte in in_buf tells how many bits in the second next
byte that will be used */
/* The leftmost unused bits in the value byte are supposed to be
zero bits */
no_bits = (int) *(++in_ptr);
val = *(++in_ptr);
counter -= 2;
if ((ret = per_insert_least_sign_bits(no_bits, val, &ptr, &unused))
== ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 20:
/* in this case the next value in_ptr points at holds the number
of following bytes that holds the value that will be inserted
in the completed buffer */
no_bytes = (int) *(++in_ptr);
counter -= (no_bytes + 1);
if ((counter < 0)
|| (ret = per_insert_octets(no_bytes, &in_ptr, &ptr,
&unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 21:
/* in this case the next two bytes in_ptr points at holds the number
of following bytes that holds the value that will be inserted
in the completed buffer */
no_bytes = (int) *(++in_ptr);
no_bytes = no_bytes << 8;
no_bytes = no_bytes | (int) *(++in_ptr);
counter -= (2 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_octets(no_bytes, &in_ptr, &ptr,
&unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 30:
/* If we call the following bytes, in the buffer in_ptr points at,
By1,By2,Rest then Rest is the value that will be transfered to
the completed buffer. By1 tells how many of the rightmost bits in
Rest that should not be used. By2 is the length of Rest in bytes.*/
in_unused = (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
counter -= (2 + no_bytes);
ret = -4711;
if ((counter < 0)
|| (ret = per_insert_octets_except_unused(no_bytes, &in_ptr,
&ptr, &unused, in_unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 31:
/* If we call the following bytes, in the buffer in_ptr points at,
By1,By2,By3,Rest then Rest is the value that will be transfered to
the completed buffer. By1 tells how many of the rightmost bits in
Rest that should not be used. By2 and By3 is the length of
Rest in bytes.*/
in_unused = (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
no_bytes = no_bytes << 8;
no_bytes = no_bytes | (int) *(++in_ptr);
counter -= (3 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_octets_except_unused(no_bytes, &in_ptr,
&ptr, &unused, in_unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 40:
/* This case implies that next byte,By1,(..,By1,By2,Bin,...)
is the desired length of the completed value, maybe needs
padding zero bits or removal of trailing zero bits from Bin.
By2 is the length of Bin and Bin is the value that will be
put into the completed buffer. Each byte in Bin has the value
1 or 0.*/
desired_len = (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
/* This is the algorithm for need of memory reallocation:
Only when padding (cases 40 - 43,45 - 47) more memory may be
used than allocated. Therefore one has to keep track of how
much of the allocated memory that has been saved, i.e. the
difference between the number of parsed bytes of the input buffer
and the number of used bytes of the output buffer.
If saved memory is less than needed for the padding then we
need more memory. */
saved_mem = buf_space - counter;
pad_bits = desired_len - no_bytes - unused;
needed = (pad_bits > 0) ? CEIL(pad_bits,8) : 0;
if (saved_mem < needed) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
)
return ASN1_ERROR;
}
counter -= (2 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_octets_as_bits_exact_len(desired_len,
no_bytes, &in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 41:
/* Same as case 40 apart from By2, the length of Bin, which is in
two bytes*/
desired_len = (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
no_bytes = no_bytes << 8;
no_bytes = no_bytes | (int) *(++in_ptr);
saved_mem = buf_space - counter;
needed = CEIL((desired_len-unused),8) - no_bytes;
if (saved_mem < needed) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
)
return ASN1_ERROR;
}
counter -= (3 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_octets_as_bits_exact_len(desired_len,
no_bytes, &in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 42:
/* Same as case 40 apart from By1, the desired length, which is in
two bytes*/
desired_len = (int) *(++in_ptr);
desired_len = desired_len << 8;
desired_len = desired_len | (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
saved_mem = buf_space - counter;
needed = CEIL((desired_len-unused),8) - no_bytes;
if (saved_mem < needed) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
)
return ASN1_ERROR;
}
counter -= (3 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_octets_as_bits_exact_len(desired_len,
no_bytes, &in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 43:
/* Same as case 40 apart from By1 and By2, the desired length and
the length of Bin, which are in two bytes each. */
desired_len = (int) *(++in_ptr);
desired_len = desired_len << 8;
desired_len = desired_len | (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
no_bytes = no_bytes << 8;
no_bytes = no_bytes | (int) *(++in_ptr);
saved_mem = buf_space - counter;
needed = CEIL((desired_len-unused),8) - no_bytes;
if (saved_mem < needed) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
)
return ASN1_ERROR;
}
counter -= (4 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_octets_as_bits_exact_len(desired_len,
no_bytes, &in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 45:
/* This case assumes that the following bytes in the incoming buffer
(called By1,By2,Bin) is By1, which is the number of bits (n) that
will be inserted in the completed buffer. By2 is the number of
bytes in Bin. Each bit in the buffer Bin should be inserted from
the leftmost until the nth.*/
desired_len = (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
saved_mem = buf_space - counter;
needed = CEIL((desired_len-unused),8) - no_bytes;
if (saved_mem < needed) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
)
return ASN1_ERROR;
}
counter -= (2 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_bits_as_bits(desired_len, no_bytes,
&in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 46:
/* Same as case 45 apart from By1, the desired length, which is
in two bytes. */
desired_len = (int) *(++in_ptr);
desired_len = desired_len << 8;
desired_len = desired_len | (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
saved_mem = buf_space - counter;
needed = CEIL((desired_len-unused),8) - no_bytes;
if (saved_mem < needed) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
)
return ASN1_ERROR;
}
counter -= (3 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_bits_as_bits(desired_len, no_bytes,
&in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
case 47:
/* Same as case 45 apart from By1 and By2, the desired length
and the length of Bin, which are in two bytes each. */
desired_len = (int) *(++in_ptr);
desired_len = desired_len << 8;
desired_len = desired_len | (int) *(++in_ptr);
no_bytes = (int) *(++in_ptr);
no_bytes = no_bytes << 8;
no_bytes = no_bytes | (int) *(++in_ptr);
saved_mem = buf_space - counter;
needed = CEIL((desired_len-unused),8) - no_bytes;
if (saved_mem < needed) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
)
return ASN1_ERROR;
}
counter -= (4 + no_bytes);
if ((counter < 0)
|| (ret = per_insert_bits_as_bits(desired_len, no_bytes,
&in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
break;
default:
return ASN1_ERROR;
}
in_ptr++;
}
/* The returned buffer must be at least one byte and
it must be octet aligned */
if ((unused == 8) && (ptr != out_binary->data))
return (ptr - out_binary->data);
else {
ptr++; /* octet align buffer */
return (ptr - out_binary->data);
}
}