aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/asn1/c_src/asn1_erl_nif.c432
-rw-r--r--lib/asn1/doc/src/asn1_ug.xml4
-rw-r--r--lib/asn1/doc/src/asn1ct.xml10
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl4
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl17
-rw-r--r--lib/asn1/src/asn1rt_ber_bin_v2.erl78
-rw-r--r--lib/asn1/src/asn1rt_nif.erl8
-rw-r--r--lib/asn1/test/asn1_bin_v2_particular_SUITE.erl.src258
8 files changed, 590 insertions, 221 deletions
diff --git a/lib/asn1/c_src/asn1_erl_nif.c b/lib/asn1/c_src/asn1_erl_nif.c
index 58314e23c2..9c9f83bc2a 100644
--- a/lib/asn1/c_src/asn1_erl_nif.c
+++ b/lib/asn1/c_src/asn1_erl_nif.c
@@ -46,10 +46,6 @@
#define ASN1_PRIMITIVE 0
#define ASN1_CONSTRUCTED 0x20
-#define ASN1_COMPLETE 1
-#define ASN1_BER_TLV_DECODE 2
-#define ASN1_BER_TLV_PARTIAL_DECODE 3
-
#define ASN1_NOVALUE 0
#define ASN1_SKIPPED 0
@@ -61,38 +57,55 @@
#define INVMASK(X,M) (X & (M ^ 0xff))
#define MASK(X,M) (X & M)
-int complete(ErlNifBinary *, unsigned char *, int );
+/* PER COMPLETE */
+int per_complete(ErlNifBinary *, unsigned char *, int);
-int insert_octets(int, unsigned char **, unsigned char **, int *);
+int per_insert_octets(int, unsigned char **, unsigned char **, int *);
-int insert_octets_except_unused(int, unsigned char **, unsigned char **, int *,
- int);
+int per_insert_octets_except_unused(int, unsigned char **, unsigned char **,
+ int *, int);
-int insert_octets_as_bits_exact_len(int, int, unsigned char **,
+int per_insert_octets_as_bits_exact_len(int, int, unsigned char **,
unsigned char **, int *);
-int insert_octets_as_bits(int, unsigned char **, unsigned char **, int *);
+int per_insert_octets_as_bits(int, unsigned char **, unsigned char **, int *);
-int pad_bits(int, unsigned char **, int *);
+int per_pad_bits(int, unsigned char **, int *);
-int insert_least_sign_bits(int, unsigned char, unsigned char **, int *);
+int per_insert_least_sign_bits(int, unsigned char, unsigned char **, int *);
-int insert_most_sign_bits(int, unsigned char, unsigned char **, int *);
+int per_insert_most_sign_bits(int, unsigned char, unsigned char **, int *);
-int insert_bits_as_bits(int, int, unsigned char **, unsigned char **, int *);
+int per_insert_bits_as_bits(int, int, unsigned char **, unsigned char **, int *);
-int insert_octets_unaligned(int, unsigned char **, unsigned char **, int);
+int per_insert_octets_unaligned(int, unsigned char **, unsigned char **, int);
-int realloc_memory(ErlNifBinary *, int, unsigned char **);
+int per_realloc_memory(ErlNifBinary *, int, unsigned char **);
-int decode_begin(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int,
+/* BER DECODE */
+int ber_decode_begin(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int,
unsigned int *);
-int decode(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int *, int);
+int ber_decode(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int *, int);
+
+int ber_decode_tag(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int, int *);
+
+int ber_decode_value(ErlNifEnv*, ERL_NIF_TERM *, unsigned char *, int *, int,
+ int);
+
+/* BER ENCODE */
+typedef struct ber_encode_mem_chunk mem_chunk_t;
+
+int ber_encode(ErlNifEnv *, ERL_NIF_TERM , mem_chunk_t **, unsigned int *);
-int decode_tag(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int, int *);
+void ber_free_chunks(mem_chunk_t *chunk);
+mem_chunk_t *ber_new_chunk(unsigned int length);
+int ber_check_memory(mem_chunk_t **curr, unsigned int needed);
-int decode_value(ErlNifEnv*, ERL_NIF_TERM *, unsigned char *, int *, int, int);
+int ber_encode_tag(ErlNifEnv *, ERL_NIF_TERM , unsigned int ,
+ mem_chunk_t **, unsigned int *);
+
+int ber_encode_length(size_t , mem_chunk_t **, unsigned int *);
/*
*
@@ -101,7 +114,8 @@ int decode_value(ErlNifEnv*, ERL_NIF_TERM *, unsigned char *, int *, int, int);
*
*/
-int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
+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 */
@@ -175,7 +189,7 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
no_bits = (int) *(++in_ptr);
val = *(++in_ptr);
counter -= 2;
- if ((ret = insert_least_sign_bits(no_bits, val, &ptr, &unused))
+ if ((ret = per_insert_least_sign_bits(no_bits, val, &ptr, &unused))
== ASN1_ERROR
)
return ASN1_ERROR;
@@ -189,8 +203,8 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
no_bytes = (int) *(++in_ptr);
counter -= (no_bytes + 1);
if ((counter < 0)
- || (ret = insert_octets(no_bytes, &in_ptr, &ptr, &unused))
- == ASN1_ERROR
+ || (ret = per_insert_octets(no_bytes, &in_ptr, &ptr,
+ &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
@@ -205,8 +219,8 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
no_bytes = no_bytes | (int) *(++in_ptr);
counter -= (2 + no_bytes);
if ((counter < 0)
- || (ret = insert_octets(no_bytes, &in_ptr, &ptr, &unused))
- == ASN1_ERROR
+ || (ret = per_insert_octets(no_bytes, &in_ptr, &ptr,
+ &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
buf_space -= ret;
@@ -222,7 +236,7 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
counter -= (2 + no_bytes);
ret = -4711;
if ((counter < 0)
- || (ret = insert_octets_except_unused(no_bytes, &in_ptr,
+ || (ret = per_insert_octets_except_unused(no_bytes, &in_ptr,
&ptr, &unused, in_unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -241,7 +255,7 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
no_bytes = no_bytes | (int) *(++in_ptr);
counter -= (3 + no_bytes);
if ((counter < 0)
- || (ret = insert_octets_except_unused(no_bytes, &in_ptr,
+ || (ret = per_insert_octets_except_unused(no_bytes, &in_ptr,
&ptr, &unused, in_unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -273,15 +287,14 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
- if (realloc_memory(out_binary, buf_size, &ptr)
- == ASN1_ERROR
- )
+ if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
+ )
return ASN1_ERROR;
}
counter -= (2 + no_bytes);
if ((counter < 0)
- || (ret = insert_octets_as_bits_exact_len(desired_len,
+ || (ret = per_insert_octets_as_bits_exact_len(desired_len,
no_bytes, &in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -302,15 +315,14 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
- if (realloc_memory(out_binary, buf_size, &ptr)
- == ASN1_ERROR
- )
+ if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
+ )
return ASN1_ERROR;
}
counter -= (3 + no_bytes);
if ((counter < 0)
- || (ret = insert_octets_as_bits_exact_len(desired_len,
+ || (ret = per_insert_octets_as_bits_exact_len(desired_len,
no_bytes, &in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -331,15 +343,14 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
- if (realloc_memory(out_binary, buf_size, &ptr)
- == ASN1_ERROR
- )
+ if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
+ )
return ASN1_ERROR;
}
counter -= (3 + no_bytes);
if ((counter < 0)
- || (ret = insert_octets_as_bits_exact_len(desired_len,
+ || (ret = per_insert_octets_as_bits_exact_len(desired_len,
no_bytes, &in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -362,15 +373,14 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
- if (realloc_memory(out_binary, buf_size, &ptr)
- == ASN1_ERROR
- )
+ if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
+ )
return ASN1_ERROR;
}
counter -= (4 + no_bytes);
if ((counter < 0)
- || (ret = insert_octets_as_bits_exact_len(desired_len,
+ || (ret = per_insert_octets_as_bits_exact_len(desired_len,
no_bytes, &in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -392,16 +402,15 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
- if (realloc_memory(out_binary, buf_size, &ptr)
- == ASN1_ERROR
- )
+ if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
+ )
return ASN1_ERROR;
}
counter -= (2 + no_bytes);
if ((counter < 0)
- || (ret = insert_bits_as_bits(desired_len, no_bytes,
+ || (ret = per_insert_bits_as_bits(desired_len, no_bytes,
&in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -422,15 +431,14 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
- if (realloc_memory(out_binary, buf_size, &ptr)
- == ASN1_ERROR
- )
+ if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
+ )
return ASN1_ERROR;
}
counter -= (3 + no_bytes);
if ((counter < 0)
- || (ret = insert_bits_as_bits(desired_len, no_bytes,
+ || (ret = per_insert_bits_as_bits(desired_len, no_bytes,
&in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -453,15 +461,14 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
/* Have to allocate more memory */
buf_size += needed;
buf_space += needed;
- if (realloc_memory(out_binary, buf_size, &ptr)
- == ASN1_ERROR
- )
+ if (per_realloc_memory(out_binary, buf_size, &ptr) == ASN1_ERROR
+ )
return ASN1_ERROR;
}
counter -= (4 + no_bytes);
if ((counter < 0)
- || (ret = insert_bits_as_bits(desired_len, no_bytes,
+ || (ret = per_insert_bits_as_bits(desired_len, no_bytes,
&in_ptr, &ptr, &unused)) == ASN1_ERROR
)
return ASN1_ERROR;
@@ -483,7 +490,7 @@ int complete(ErlNifBinary *out_binary, unsigned char *in_buf, int in_buf_len) {
}
}
-int realloc_memory(ErlNifBinary *binary, int amount, unsigned char **ptr) {
+int per_realloc_memory(ErlNifBinary *binary, int amount, unsigned char **ptr) {
int i = *ptr - binary->data;
@@ -496,7 +503,7 @@ int realloc_memory(ErlNifBinary *binary, int amount, unsigned char **ptr) {
return ASN1_OK;
}
-int insert_most_sign_bits(int no_bits, unsigned char val,
+int per_insert_most_sign_bits(int no_bits, unsigned char val,
unsigned char **output_ptr, int *unused) {
unsigned char *ptr = *output_ptr;
@@ -517,7 +524,7 @@ int insert_most_sign_bits(int no_bits, unsigned char val,
return ASN1_OK;
}
-int insert_least_sign_bits(int no_bits, unsigned char val,
+int per_insert_least_sign_bits(int no_bits, unsigned char val,
unsigned char **output_ptr, int *unused) {
unsigned char *ptr = *output_ptr;
int ret = 0;
@@ -543,10 +550,10 @@ int insert_least_sign_bits(int no_bits, unsigned char val,
return ret;
}
-/* pad_bits adds no_bits bits in the buffer that output_ptr
+/* per_pad_bits adds no_bits bits in the buffer that output_ptr
points at.
*/
-int pad_bits(int no_bits, unsigned char **output_ptr, int *unused) {
+int per_pad_bits(int no_bits, unsigned char **output_ptr, int *unused) {
unsigned char *ptr = *output_ptr;
int ret = 0;
@@ -569,37 +576,37 @@ int pad_bits(int no_bits, unsigned char **output_ptr, int *unused) {
The unused parameter tells how many bits that are not set in the
actual byte in the output buffer. If desired_no is more bits than the
input buffer has in no_bytes bytes, then zero bits is padded.*/
-int insert_bits_as_bits(int desired_no, int no_bytes, unsigned char **input_ptr,
- unsigned char **output_ptr, int *unused) {
+int per_insert_bits_as_bits(int desired_no, int no_bytes,
+ unsigned char **input_ptr, unsigned char **output_ptr, int *unused) {
unsigned char *in_ptr = *input_ptr;
unsigned char val;
int no_bits, ret, ret2;
if (desired_no == (no_bytes * 8)) {
- if (insert_octets_unaligned(no_bytes, &in_ptr, output_ptr, *unused)
+ if (per_insert_octets_unaligned(no_bytes, &in_ptr, output_ptr, *unused)
== ASN1_ERROR
)
return ASN1_ERROR;
ret = no_bytes;
} else if (desired_no < (no_bytes * 8)) {
- /* printf("insert_bits_as_bits 1\n\r"); */
- if (insert_octets_unaligned(desired_no / 8, &in_ptr, output_ptr,
+ /* printf("per_insert_bits_as_bits 1\n\r"); */
+ if (per_insert_octets_unaligned(desired_no / 8, &in_ptr, output_ptr,
*unused) == ASN1_ERROR
)
return ASN1_ERROR;
- /* printf("insert_bits_as_bits 2\n\r"); */
+ /* printf("per_insert_bits_as_bits 2\n\r"); */
val = *++in_ptr;
/* printf("val = %d\n\r",(int)val); */
no_bits = desired_no % 8;
/* printf("no_bits = %d\n\r",no_bits); */
- insert_most_sign_bits(no_bits, val, output_ptr, unused);
+ per_insert_most_sign_bits(no_bits, val, output_ptr, unused);
ret = CEIL(desired_no,8);
} else {
- if (insert_octets_unaligned(no_bytes, &in_ptr, output_ptr, *unused)
+ if (per_insert_octets_unaligned(no_bytes, &in_ptr, output_ptr, *unused)
== ASN1_ERROR
)
return ASN1_ERROR;
- ret2 = pad_bits(desired_no - (no_bytes * 8), output_ptr, unused);
+ ret2 = per_pad_bits(desired_no - (no_bytes * 8), output_ptr, unused);
/* printf("ret2 = %d\n\r",ret2); */
ret = CEIL(desired_no,8);
/* printf("ret = %d\n\r",ret); */
@@ -609,30 +616,30 @@ int insert_bits_as_bits(int desired_no, int no_bytes, unsigned char **input_ptr,
return ret;
}
-/* insert_octets_as_bits_exact_len */
-int insert_octets_as_bits_exact_len(int desired_len, int in_buff_len,
+/* per_insert_octets_as_bits_exact_len */
+int per_insert_octets_as_bits_exact_len(int desired_len, int in_buff_len,
unsigned char **in_ptr, unsigned char **ptr, int *unused) {
int ret = 0;
int ret2 = 0;
if (desired_len == in_buff_len) {
- if ((ret = insert_octets_as_bits(in_buff_len, in_ptr, ptr, unused))
+ if ((ret = per_insert_octets_as_bits(in_buff_len, in_ptr, ptr, unused))
== ASN1_ERROR
)
return ASN1_ERROR;
} else if (desired_len > in_buff_len) {
- if ((ret = insert_octets_as_bits(in_buff_len, in_ptr, ptr, unused))
+ if ((ret = per_insert_octets_as_bits(in_buff_len, in_ptr, ptr, unused))
== ASN1_ERROR
)
return ASN1_ERROR;
/* now pad with zero bits */
/* printf("~npad_bits: called with %d bits padding~n~n~r",desired_len - in_buff_len); */
- if ((ret2 = pad_bits(desired_len - in_buff_len, ptr, unused))
+ if ((ret2 = per_pad_bits(desired_len - in_buff_len, ptr, unused))
== ASN1_ERROR
)
return ASN1_ERROR;
} else {/* desired_len < no_bits */
- if ((ret = insert_octets_as_bits(desired_len, in_ptr, ptr, unused))
+ if ((ret = per_insert_octets_as_bits(desired_len, in_ptr, ptr, unused))
== ASN1_ERROR
)
return ASN1_ERROR;
@@ -648,7 +655,7 @@ int insert_octets_as_bits_exact_len(int desired_len, int in_buff_len,
otherwise the function returns ASN1_ERROR. The output buffer is concatenated
without alignment.
*/
-int insert_octets_as_bits(int no_bytes, unsigned char **input_ptr,
+int per_insert_octets_as_bits(int no_bytes, unsigned char **input_ptr,
unsigned char **output_ptr, int *unused) {
unsigned char *in_ptr = *input_ptr;
unsigned char *ptr = *output_ptr;
@@ -688,7 +695,7 @@ int insert_octets_as_bits(int no_bytes, unsigned char **input_ptr,
into the output buffer, *output_ptr. Before the first byte is
inserted the input buffer is aligned.
*/
-int insert_octets(int no_bytes, unsigned char **input_ptr,
+int per_insert_octets(int no_bytes, unsigned char **input_ptr,
unsigned char **output_ptr, int *unused) {
unsigned char *in_ptr = *input_ptr;
unsigned char *ptr = *output_ptr;
@@ -710,10 +717,10 @@ int insert_octets(int no_bytes, unsigned char **input_ptr,
return (ret + no_bytes);
}
-/* insert_octets_unaligned inserts bytes from the input buffer, *input_ptr,
+/* per_insert_octets_unaligned inserts bytes from the input buffer, *input_ptr,
into the output buffer, *output_ptr.No alignment is done.
*/
-int insert_octets_unaligned(int no_bytes, unsigned char **input_ptr,
+int per_insert_octets_unaligned(int no_bytes, unsigned char **input_ptr,
unsigned char **output_ptr, int unused) {
unsigned char *in_ptr = *input_ptr;
unsigned char *ptr = *output_ptr;
@@ -737,7 +744,7 @@ int insert_octets_unaligned(int no_bytes, unsigned char **input_ptr,
return no_bytes;
}
-int insert_octets_except_unused(int no_bytes, unsigned char **input_ptr,
+int per_insert_octets_except_unused(int no_bytes, unsigned char **input_ptr,
unsigned char **output_ptr, int *unused, int in_unused) {
unsigned char *in_ptr = *input_ptr;
unsigned char *ptr = *output_ptr;
@@ -745,12 +752,12 @@ int insert_octets_except_unused(int no_bytes, unsigned char **input_ptr,
int ret = 0;
if (in_unused == 0) {
- if ((ret = insert_octets_unaligned(no_bytes, &in_ptr, &ptr, *unused))
+ if ((ret = per_insert_octets_unaligned(no_bytes, &in_ptr, &ptr, *unused))
== ASN1_ERROR
)
return ASN1_ERROR;
} else {
- if ((ret = insert_octets_unaligned(no_bytes - 1, &in_ptr, &ptr, *unused))
+ if ((ret = per_insert_octets_unaligned(no_bytes - 1, &in_ptr, &ptr, *unused))
!= ASN1_ERROR) {
val = (int) *(++in_ptr);
no_bits = 8 - in_unused;
@@ -789,7 +796,7 @@ int insert_octets_except_unused(int no_bytes, unsigned char **input_ptr,
/*
* int decode(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
- int in_buf_len, unsigned int *err_pos)
+ int in_buf_len, unsigned int *err_pos)
* term is a pointer to the term which is to be returned to erlang
* in_buf is a pointer into the buffer of incoming bytes.
* in_buf_len is the length of the incoming buffer.
@@ -830,16 +837,16 @@ int insert_octets_except_unused(int no_bytes, unsigned char **input_ptr,
* is the empty binary.
* If some error occured during the decoding of the in_buf an error is returned.
*/
-int decode_begin(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
+int ber_decode_begin(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
int in_buf_len, unsigned int *err_pos) {
int maybe_ret;
int ib_index = 0;
unsigned char *rest_data;
ERL_NIF_TERM decoded_term, rest;
- if ((maybe_ret = decode(env, &decoded_term, in_buf, &ib_index, in_buf_len))
- <= ASN1_ERROR)
- {
+ if ((maybe_ret = ber_decode(env, &decoded_term, in_buf, &ib_index,
+ in_buf_len)) <= ASN1_ERROR)
+ {
*err_pos = ib_index;
return maybe_ret;
};
@@ -855,7 +862,7 @@ int decode_begin(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
return ASN1_OK;
}
-int decode(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
+int ber_decode(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
int *ib_index, int in_buf_len) {
int maybe_ret;
int form;
@@ -865,7 +872,7 @@ int decode(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
if ((*ib_index + 2) > in_buf_len)
return ASN1_VALUE_ERROR;
/* "{{TagNo," */
- if ((form = decode_tag(env, &tag, in_buf, in_buf_len, ib_index))
+ if ((form = ber_decode_tag(env, &tag, in_buf, in_buf_len, ib_index))
<= ASN1_ERROR
)
return form; /* 5 bytes */
@@ -875,7 +882,7 @@ int decode(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
/* buffer must hold at least one byte (0 as length and nothing as
value) */
/* "{{TagNo,Value}," */
- if ((maybe_ret = decode_value(env, &value, in_buf, ib_index, form,
+ if ((maybe_ret = ber_decode_value(env, &value, in_buf, ib_index, form,
in_buf_len)) <= ASN1_ERROR
)
return maybe_ret; /* at least 5 bytes */
@@ -887,7 +894,7 @@ int decode(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf,
* decode_tag decodes the BER encoded tag in in_buf and creates an
* nif term tag
*/
-int decode_tag(ErlNifEnv* env, ERL_NIF_TERM *tag, unsigned char *in_buf,
+int ber_decode_tag(ErlNifEnv* env, ERL_NIF_TERM *tag, unsigned char *in_buf,
int in_buf_len, int *ib_index) {
int tag_no, tmp_tag, form;
@@ -930,11 +937,11 @@ int decode_tag(ErlNifEnv* env, ERL_NIF_TERM *tag, unsigned char *in_buf,
}
/*
- * decode_value decodes the BER encoded length and value fields in the
+ * ber_decode_value decodes the BER encoded length and value fields in the
* in_buf and puts the value part in the decode_buf as an Erlang
* nif term into value
*/
-int decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *in_buf,
+int ber_decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *in_buf,
int *ib_index, int form, int in_buf_len) {
int maybe_ret;
unsigned int len = 0;
@@ -970,7 +977,7 @@ int decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *in_buf,
if (*ib_index >= in_buf_len)
return ASN1_INDEF_LEN_ERROR;
- if ((maybe_ret = decode(env, &term, in_buf, ib_index, in_buf_len))
+ if ((maybe_ret = ber_decode(env, &term, in_buf, ib_index, in_buf_len))
<= ASN1_ERROR
)
return maybe_ret;
@@ -986,9 +993,9 @@ int decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *in_buf,
curr_head = enif_make_list(env, 0);
while (*ib_index < end_index) {
- if ((maybe_ret = decode(env, &term, in_buf, ib_index, in_buf_len))
- <= ASN1_ERROR
- )
+ if ((maybe_ret = ber_decode(env, &term, in_buf, ib_index,
+ in_buf_len)) <= ASN1_ERROR
+ )
return maybe_ret;
curr_head = enif_make_list_cell(env, term, curr_head);
}
@@ -1003,6 +1010,187 @@ int decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *in_buf,
return ASN1_OK;
}
+struct ber_encode_mem_chunk {
+ mem_chunk_t *next;
+ int length;
+ char *top;
+ char *curr;
+};
+
+int ber_encode(ErlNifEnv *env, ERL_NIF_TERM term, mem_chunk_t **curr, unsigned int *count) {
+
+ const ERL_NIF_TERM *tv;
+ unsigned int form;
+ int arity;
+
+ if (!enif_get_tuple(env, term, &arity, &tv))
+ return ASN1_ERROR;
+
+ form = enif_is_list(env, tv[1]) ? ASN1_CONSTRUCTED : ASN1_PRIMITIVE;
+
+ switch (form) {
+ case ASN1_PRIMITIVE: {
+ ErlNifBinary value;
+ if (!enif_inspect_binary(env, tv[1], &value))
+ return ASN1_ERROR;
+
+ if (ber_check_memory(curr, value.size))
+ return ASN1_ERROR;
+ memcpy((*curr)->curr - value.size + 1, value.data, value.size);
+ (*curr)->curr -= value.size;
+ *count += value.size;
+
+ if (ber_encode_length(value.size, curr, count))
+ return ASN1_ERROR;
+
+ break;
+ }
+ case ASN1_CONSTRUCTED: {
+ ERL_NIF_TERM head, tail;
+ unsigned int tmp_cnt;
+
+ if(!enif_make_reverse_list(env, tv[1], &head))
+ return ASN1_ERROR;
+
+ if (!enif_get_list_cell(env, head, &head, &tail)) {
+ if (enif_is_empty_list(env, tv[1])) {
+ *((*curr)->curr) = 0;
+ (*curr)->curr -= 1;
+ (*count)++;
+ break;
+ } else
+ return ASN1_ERROR;
+ }
+
+ do {
+ tmp_cnt = 0;
+ if (ber_encode(env, head, curr, &tmp_cnt)) {
+ return ASN1_ERROR;
+ }
+ *count += tmp_cnt;
+ } while (enif_get_list_cell(env, tail, &head, &tail));
+
+ if (ber_check_memory(curr, *count)) {
+ return ASN1_ERROR;
+ }
+
+ if (ber_encode_length(*count, curr, count)) {
+ return ASN1_ERROR;
+ }
+
+ break;
+ }
+ }
+
+ // We need atleast 5 bytes to encode the next tlv
+ if (ber_check_memory(curr, 3))
+ return ASN1_ERROR;
+
+ if (ber_encode_tag(env, tv[0], form, curr, count))
+ return ASN1_ERROR;
+
+ return ASN1_OK;
+}
+
+int ber_encode_tag(ErlNifEnv *env, ERL_NIF_TERM tag, unsigned int form,
+ mem_chunk_t **curr, unsigned int *count) {
+ unsigned int class_tag_no, head_tag;
+ if (!enif_get_uint(env, tag, &class_tag_no))
+ return ASN1_ERROR;
+
+ head_tag = form | ((class_tag_no & 0x30000) >> 10);
+ class_tag_no = class_tag_no & 0xFFFF;
+
+ if (class_tag_no <= 30) {
+ *(*curr)->curr = head_tag | class_tag_no;
+ (*curr)->curr -= 1;
+ (*count)++;
+ return ASN1_OK;
+ } else {
+ *(*curr)->curr = class_tag_no & 127;
+ class_tag_no = class_tag_no >> 7;
+ (*curr)->curr -= 1;
+ (*count)++;
+
+ while (class_tag_no > 0) {
+ *(*curr)->curr = (class_tag_no & 127) | 0x80;
+ class_tag_no >>= 7;
+ (*curr)->curr -= 1;
+ (*count)++;
+ }
+
+ *(*curr)->curr = head_tag | 0x1F;
+ (*curr)->curr -= 1;
+ (*count)++;
+
+ return ASN1_OK;
+ }
+}
+
+int ber_encode_length(size_t size, mem_chunk_t **curr, unsigned int *count) {
+ if (size < 128) {
+ if (ber_check_memory(curr, 1u))
+ return ASN1_ERROR;
+ *(*curr)->curr = size;
+ (*curr)->curr -= 1;
+ (*count)++;
+ } else {
+ int chunks = size / 256 + 1;
+ if (ber_check_memory(curr, chunks + 1))
+ return ASN1_ERROR;
+
+ while (size > 0)
+ {
+ *(*curr)->curr = size & 0xFF;
+ size >>= 8;
+ (*curr)->curr -= 1;
+ (*count)++;
+ }
+
+ *(*curr)->curr = chunks | 0x80;
+ (*curr)->curr -= 1;
+ (*count)++;
+ }
+ return ASN1_OK;
+}
+
+mem_chunk_t *ber_new_chunk(unsigned int length) {
+ mem_chunk_t *new = enif_alloc(sizeof(mem_chunk_t));
+ if (new == NULL)
+ return NULL;
+ new->next = NULL;
+ new->top = enif_alloc(sizeof(char) * length);
+ if (new->top == NULL) {
+ free(new);
+ return NULL;
+ }
+ new->curr = new->top + length - 1;
+ new->length = length;
+ return new;
+}
+
+void ber_free_chunks(mem_chunk_t *chunk) {
+ mem_chunk_t *curr, *next = chunk;
+ while (next != NULL) {
+ curr = next;
+ next = curr->next;
+ enif_free(curr->top);
+ enif_free(curr);
+ }
+}
+
+int ber_check_memory(mem_chunk_t **curr, unsigned int needed) {
+ mem_chunk_t *new;
+ if ((*curr)->curr-needed >= (*curr)->top)
+ return ASN1_OK;
+
+ if ((new = ber_new_chunk((*curr)->length > needed ? (*curr)->length * 2 : (*curr)->length + needed)) == NULL)
+ return ASN1_ERROR;
+ new->next = *curr;
+ *curr = new;
+ return ASN1_OK;
+}
+
static ERL_NIF_TERM encode_per_complete(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[]) {
ERL_NIF_TERM err_code;
@@ -1018,11 +1206,11 @@ static ERL_NIF_TERM encode_per_complete(ErlNifEnv* env, int argc,
if (in_binary.size == 0)
return enif_make_binary(env, &out_binary);
- if ((complete_len = complete(&out_binary, in_binary.data, in_binary.size))
- <= ASN1_ERROR) {
+ if ((complete_len = per_complete(&out_binary, in_binary.data,
+ in_binary.size)) <= ASN1_ERROR) {
enif_release_binary(&out_binary);
if (complete_len == ASN1_ERROR
- )
+ )
err_code = enif_make_uint(env, '1');
else
err_code = enif_make_uint(env, 0);
@@ -1043,7 +1231,7 @@ static ERL_NIF_TERM decode_ber_tlv(ErlNifEnv* env, int argc,
if (!enif_inspect_iolist_as_binary(env, argv[0], &in_binary))
return enif_make_badarg(env);
- if ((return_code = decode_begin(env, &return_term, in_binary.data,
+ if ((return_code = ber_decode_begin(env, &return_term, in_binary.data,
in_binary.size, &err_pos)) != ASN1_OK
)
return enif_make_tuple2(env, enif_make_atom(env,"error"), enif_make_tuple2(env,
@@ -1051,6 +1239,43 @@ static ERL_NIF_TERM decode_ber_tlv(ErlNifEnv* env, int argc,
return return_term;
}
+static ERL_NIF_TERM encode_ber_tlv(ErlNifEnv* env, int argc,
+ const ERL_NIF_TERM argv[]) {
+ ErlNifBinary out_binary;
+ unsigned int length = 0, pos = 0;
+ int encode_err;
+ mem_chunk_t *curr, *top;
+ ERL_NIF_TERM err_code;
+
+ curr = ber_new_chunk(40);
+
+ if ((encode_err = ber_encode(env, argv[0], &curr, &length))
+ <= ASN1_ERROR) {
+ ber_free_chunks(curr);
+ err_code = enif_make_int(env, encode_err);
+ return enif_make_tuple2(env, enif_make_atom(env, "error"), err_code);
+ }
+
+ if (!enif_alloc_binary(length, &out_binary)) {
+ ber_free_chunks(curr);
+ return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_atom(env,"oom"));
+ }
+
+ top = curr;
+
+ while (curr != NULL) {
+ length = curr->length - (curr->curr-curr->top) -1;
+ if (length > 0)
+ memcpy(out_binary.data + pos, curr->curr+1, length);
+ pos += length;
+ curr = curr->next;
+ }
+
+ ber_free_chunks(top);
+
+ return enif_make_binary(env, &out_binary);
+}
+
static int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) {
int i;
return enif_get_int(env, load_info, &i) && i == 1;
@@ -1074,6 +1299,7 @@ static void unload(ErlNifEnv* env, void* priv_data) {
}
static ErlNifFunc nif_funcs[] = { { "encode_per_complete", 1,
- encode_per_complete }, { "decode_ber_tlv", 1, decode_ber_tlv } };
+ encode_per_complete }, { "decode_ber_tlv", 1, decode_ber_tlv }, {
+ "encode_ber_tlv", 1, encode_ber_tlv } };
ERL_NIF_INIT(asn1rt_nif, nif_funcs, load, NULL, upgrade, unload)
diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml
index 61e4f6062f..1b399fb641 100644
--- a/lib/asn1/doc/src/asn1_ug.xml
+++ b/lib/asn1/doc/src/asn1_ug.xml
@@ -369,7 +369,7 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
<item>
<p>Together with the flags <c>ber_bin</c>
and <c>optimize</c> you choose to use a nif for considerable
- faster decode. </p>
+ faster encode and decode. </p>
</item>
<tag><c>+asn1config</c></tag>
<item>
@@ -631,7 +631,7 @@ asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
</table>
<p>
- The sole compile options <c>ber</c>, <c>ber_bin</c>, <c>per</c> and
+ The compile options <c>ber</c>, <c>per</c> and
<c>driver</c> are kept for backwards compatibility and should not be
used in new code. The nif implementation which replaces the linked-in
driver has been shown to be about 5-15% faster.
diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml
index d7c2572dc8..13e6977419 100644
--- a/lib/asn1/doc/src/asn1ct.xml
+++ b/lib/asn1/doc/src/asn1ct.xml
@@ -225,8 +225,8 @@ Binary = binary()
<item>
<p>Option valid together with <c>ber_bin</c> and <c>optimize</c>
options. It enables the use of several nifs that gives faster
- decode. Nifs are only enabled by the explicit use of the option
- <c>nif</c></p>
+ encode and decode. Nifs are only enabled by the explicit use of
+ the option <c>nif</c></p>
</item>
<tag><c>asn1config</c></tag>
<item>
@@ -269,7 +269,11 @@ Binary = binary()
<c>.set.asn</c> are exported, unless a
<c>{export,[atom()]}</c> or <c>{export_all,true}</c> option
are provided. The list of atoms are names of chosen asn1
- specs from the <c>.set.asn</c> file.</p>
+ specs from the <c>.set.asn</c> file. </p>
+ <p>When used together with <c>nif</c> for <c>ber_bin</c>, the
+ asn1 nifs will be used if the <c>asn1rt_nif</c> module is
+ available. If it is not available, a slower erlang fallback
+ will be used.</p>
</item>
<tag><c>inline</c></tag>
<item>
diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
index e16873c717..243ff234a7 100644
--- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
@@ -877,13 +877,13 @@ gen_dec_choice(Erules,TopType, _ChTag, CompList, Ext) ->
emit([indent(9),"exit({error,{asn1,{invalid_choice_tag,",
{curr,else},"}}})",nl]);
_ ->
- emit([indent(9),"{asn1_ExtAlt, ?RT_BER:encode(",{curr,else},")}",nl])
+ emit([indent(9),"{asn1_ExtAlt, ?RT_BER:encode(",{curr,else},
+ asn1ct_gen:nif_parameter(),")}",nl])
end,
emit([indent(3),"end",nl]),
asn1ct_name:new(tag),
asn1ct_name:new(else).
-
gen_dec_choice_cases(_Erules,_TopType, []) ->
ok;
gen_dec_choice_cases(Erules,TopType, [H|T]) ->
diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
index e8a4ad0cf1..781271bae7 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -728,13 +728,13 @@ gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Form,OptOrMand) ->
{_,#'ObjectClassFieldType'{}} ->
case asn1ct_gen:get_inner(Att#type.def) of
'ASN1_OPEN_TYPE' ->
- emit([{asis,DoTag},")"]);
+ emit([{asis,DoTag},asn1ct_gen:nif_parameter(),")"]);
_ -> ok
end;
{{string,TagStr},'ASN1_OPEN_TYPE'} ->
- emit([TagStr,")"]);
+ emit([TagStr,asn1ct_gen:nif_parameter(),")"]);
{_,'ASN1_OPEN_TYPE'} ->
- emit([{asis,DoTag},")"]);
+ emit([{asis,DoTag},asn1ct_gen:nif_parameter(),")"]);
{{string,TagStr},_} ->
emit([TagStr,")"]);
_ when is_list(DoTag) ->
@@ -1502,13 +1502,14 @@ gen_objset_dec(Erules,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
_ClFields,_NthObj) ->
emit(["'getdec_",ObjSetName,"'(_, _) ->",nl]),
emit([indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]),
+
case Erules of
ber_bin_v2 ->
emit([indent(4),"case Bytes of",nl,
indent(6),"Bin when is_binary(Bin) -> ",nl,
indent(8),"Bin;",nl,
indent(6),"_ ->",nl,
- indent(8),"?RT_BER:encode(Bytes)",nl,
+ indent(8),"?RT_BER:encode(Bytes",driver_parameter(),")",nl,
indent(4),"end",nl]);
_ ->
emit([indent(6),"Len = case Bytes of",nl,indent(9),
@@ -1521,6 +1522,14 @@ gen_objset_dec(Erules,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
gen_objset_dec(_,_,_,[],_,_,_) ->
ok.
+driver_parameter() ->
+ Options = get(encoding_options),
+ case {lists:member(driver,Options),lists:member(nif,Options)} of
+ {true,_} -> ",nif";
+ {_,true} -> ",nif";
+ _ -> ",erlang"
+ end.
+
emit_default_getdec(ObjSetName,UniqueName) ->
emit(["'getdec_",ObjSetName,"'(",{asis,UniqueName},", ErrV) ->",nl]),
emit([indent(2), "fun(C,V,_) -> exit({{component,C},{value,V},{unique_name_and_value,",{asis,UniqueName},", ErrV}}) end"]).
diff --git a/lib/asn1/src/asn1rt_ber_bin_v2.erl b/lib/asn1/src/asn1rt_ber_bin_v2.erl
index 2df289e2b8..bef2216091 100644
--- a/lib/asn1/src/asn1rt_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1rt_ber_bin_v2.erl
@@ -21,7 +21,7 @@
%% encoding / decoding of BER
--export([decode/1, decode/2, match_tags/2, encode/1]).
+-export([decode/1, decode/2, match_tags/2, encode/1, encode/2]).
-export([fixoptionals/2, cindex/3,
list_to_record/2,
encode_tag_val/1,
@@ -50,10 +50,13 @@
-export([encode_open_type/1,encode_open_type/2,
decode_open_type/2,decode_open_type/3,
- decode_open_type_as_binary/2]).
+ decode_open_type_as_binary/2,
+ decode_open_type_as_binary/3]).
-export([decode_primitive_incomplete/2,decode_selective/2]).
-
+
+-export([is_nif_loadable/0]).
+
-include("asn1_records.hrl").
% the encoding of class of tag bits 8 and 7
@@ -126,15 +129,28 @@
% encode(Tlv) ->
% encode_constructed(Tlv).
-encode([Tlv]) ->
- encode(Tlv);
-encode({TlvTag,TlvVal}) when is_list(TlvVal) ->
+encode(Tlv) ->
+ encode(Tlv,erlang).
+
+encode(Tlv,_) when is_binary(Tlv) ->
+ Tlv;
+encode([Tlv],Method) ->
+ encode(Tlv,Method);
+encode(Tlv, nif) ->
+ case is_nif_loadable() of
+ true ->
+ asn1rt_nif:encode_ber_tlv(Tlv);
+ false ->
+ encode_erl(Tlv)
+ end;
+encode(Tlv, _) ->
+ encode_erl(Tlv).
+
+encode_erl({TlvTag,TlvVal}) when is_list(TlvVal) ->
%% constructed form of value
encode_tlv(TlvTag,TlvVal,?CONSTRUCTED);
-encode({TlvTag,TlvVal}) ->
- encode_tlv(TlvTag,TlvVal,?PRIMITIVE);
-encode(Bin) when is_binary(Bin) ->
- Bin.
+encode_erl({TlvTag,TlvVal}) ->
+ encode_tlv(TlvTag,TlvVal,?PRIMITIVE).
encode_tlv(TlvTag,TlvVal,Form) ->
Tag = encode_tlv_tag(TlvTag,Form),
@@ -153,7 +169,7 @@ encode_tlv_val(Bin) ->
{Bin,size(Bin)}.
encode_tlv_list([Tlv|Tlvs],Acc) ->
- EncTlv = encode(Tlv),
+ EncTlv = encode_erl(Tlv),
encode_tlv_list(Tlvs,[EncTlv|Acc]);
encode_tlv_list([],Acc) ->
Bin=list_to_binary(lists:reverse(Acc)),
@@ -164,28 +180,36 @@ decode(B) ->
%% asn1-1.7
decode(B, nif) ->
- case application:get_env(asn1, nif_loadable) of
- {ok, true} ->
+ case is_nif_loadable() of
+ true ->
case asn1rt_nif:decode_ber_tlv(B) of
{error, Reason} -> handle_error(Reason, B);
Else -> Else
end;
- {ok, false} ->
- decode(B);
- undefined ->
- case catch code:load_file(asn1rt_nif) of
- {module, asn1rt_nif} ->
- application:set_env(asn1, nif_loadable, true);
- _Else ->
- application:set_env(asn1, nif_loadable, false)
- end,
- decode(B, nif)
+ false ->
+ decode(B)
end;
decode(B,erlang) when is_binary(B) ->
decode_primitive(B);
decode(Tlv,erlang) ->
{Tlv,<<>>}.
+%% Have to check this since asn1 is not guaranteed to be available
+is_nif_loadable() ->
+ case application:get_env(asn1, nif_loadable) of
+ {ok,R} ->
+ R;
+ undefined ->
+ case catch code:load_file(asn1rt_nif) of
+ {module, asn1rt_nif} ->
+ application:set_env(asn1, nif_loadable, true),
+ true;
+ _Else ->
+ application:set_env(asn1, nif_loadable, false),
+ false
+ end
+ end.
+
handle_error([],_)->
exit({error,{asn1,{"memory allocation problem"}}});
handle_error({$1,_},L) -> % error in nif
@@ -780,12 +804,14 @@ decode_open_type(Tlv, TagIn, Method) ->
end.
-decode_open_type_as_binary(Tlv,TagIn)->
+decode_open_type_as_binary(Tlv, TagIn) ->
+ decode_open_type_as_binary(Tlv, TagIn, erlang).
+decode_open_type_as_binary(Tlv,TagIn, Method)->
case match_tags(Tlv,TagIn) of
V when is_binary(V) ->
V;
- [Tlv2] -> encode(Tlv2);
- Tlv2 -> encode(Tlv2)
+ [Tlv2] -> encode(Tlv2, Method);
+ Tlv2 -> encode(Tlv2, Method)
end.
%%===============================================================================
diff --git a/lib/asn1/src/asn1rt_nif.erl b/lib/asn1/src/asn1rt_nif.erl
index 8580c70e6b..de1fb94816 100644
--- a/lib/asn1/src/asn1rt_nif.erl
+++ b/lib/asn1/src/asn1rt_nif.erl
@@ -22,7 +22,8 @@
%% Nif interface for asn1
-export([encode_per_complete/1,
- decode_ber_tlv/1]).
+ decode_ber_tlv/1,
+ encode_ber_tlv/1]).
-on_load(load_nif/0).
@@ -76,8 +77,11 @@ load_nif() ->
Status
end.
-encode_per_complete(_Binary) ->
+encode_per_complete(_TagValueList) ->
erlang:nif_error({nif_not_loaded,module,?MODULE,line,?LINE}).
decode_ber_tlv(_Binary) ->
erlang:nif_error({nif_not_loaded,module,?MODULE,line,?LINE}).
+
+encode_ber_tlv(_TagValueList) ->
+ erlang:nif_error({nif_not_loaded,module,?MODULE,line,?LINE}).
diff --git a/lib/asn1/test/asn1_bin_v2_particular_SUITE.erl.src b/lib/asn1/test/asn1_bin_v2_particular_SUITE.erl.src
index 6e15aa9fdc..4c3c8c7808 100644
--- a/lib/asn1/test/asn1_bin_v2_particular_SUITE.erl.src
+++ b/lib/asn1/test/asn1_bin_v2_particular_SUITE.erl.src
@@ -1,5 +1,5 @@
-particular() -> [smp, per_performance, ber_performance, ticket7904].
+particular() -> [smp, ticket7904].
smp(suite) -> [];
@@ -34,104 +34,186 @@ smp(Config) ->
end.
per_performance(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ NifDir = filename:join(PrivDir,"nif"),
+ ErlDir = filename:join(PrivDir,"erl"),
+ file:make_dir(NifDir),file:make_dir(ErlDir),
?line Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
- ?line ok = testNBAPsystem:compile(Config,per_bin,[optimize]),
- NumOfProcs = erlang:system_info(schedulers)*10,
- N = 10000,
- SmpN = lists:seq(1,round(N/NumOfProcs)),
-
- asn1_wrapper:encode('NBAP-PDU-Discriptions','NBAP-PDU', Msg),
-
- PerFun = fun() ->
- [asn1_wrapper:encode('NBAP-PDU-Discriptions',
- 'NBAP-PDU',
- Msg) || _I <- lists:seq(1,N)],
- ok
+ ?line ok = testNBAPsystem:compile([{priv_dir,NifDir}|Config],per_bin,
+ [optimize]),
+ ?line ok = testNBAPsystem:compile([{priv_dir,ErlDir}|Config],per_bin,
+ []),
+
+ Modules = ['NBAP-CommonDataTypes',
+ 'NBAP-Constants',
+ 'NBAP-Containers',
+ 'NBAP-IEs',
+ 'NBAP-PDU-Contents',
+ 'NBAP-PDU-Discriptions'],
+
+
+ PreNif = fun() ->
+ code:add_patha(NifDir),
+ lists:foreach(fun(M) ->
+ code:purge(M),
+ code:load_file(M)
+ end,Modules)
+ end,
+
+ PreErl = fun() ->
+ code:add_patha(ErlDir),
+ lists:foreach(fun(M) ->
+ code:purge(M),
+ code:load_file(M)
+ end,Modules)
end,
- PerSMPFun =
- fun() ->
- pforeach(fun(_) ->
- [asn1_wrapper:encode('NBAP-PDU-Discriptions',
- 'NBAP-PDU',
- Msg) || _I <- SmpN]
- end,lists:seq(1,NumOfProcs))
- end,
-
- ?line {TimeN,ok} = timer:tc(PerFun),
- ?line {TimeNS,ok} = timer:tc(PerSMPFun),
-
- ?line ok = testNBAPsystem:compile(Config,per_bin,[]),
-
- ?line {TimeE,ok} = timer:tc(PerFun),
- ?line {TimeES,ok} = timer:tc(PerSMPFun),
-
- ct:log("Seq:<br/>"
- "Nif : ~p (~.2f%)<br/>"
- "Erlang: ~p (~.2f%)<br/>"
- "Parallel:<br/>"
- "Nif : ~p (~.2f%)<br/>"
- "Erlang: ~p (~.2f%)<br/>",
- [TimeN,TimeN/TimeN*100,
- TimeE,TimeE/TimeN*100,
- TimeNS,TimeNS/TimeNS*100,
- TimeES,TimeES/TimeNS*100]),
-
- {comment, lists:flatten(io_lib:format("Nifs are ~.2f% faster than erlang!",
- [faster(TimeN+TimeNS,
- TimeE+TimeES)]))}.
+ Func = fun() ->
+ element(1,timer:tc(
+ asn1_wrapper,encode,['NBAP-PDU-Discriptions',
+ 'NBAP-PDU',
+ Msg]))
+ end,
+
+ nif_vs_erlang_performance({{{PreNif,Func},{PreErl,Func}},100000,32}).
ber_performance(Config) ->
?line Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
?line ok = testNBAPsystem:compile(Config,ber_bin,[optimize,nif]),
- NumOfProcs = erlang:system_info(schedulers)*10,
- N = 10000,
- SmpN = lists:seq(1,round(N/NumOfProcs)),
- {ok,B} = asn1_wrapper:encode('NBAP-PDU-Discriptions','NBAP-PDU', Msg),
BerFun = fun() ->
- [asn1_wrapper:decode(
+ {ok,B} = asn1_wrapper:encode('NBAP-PDU-Discriptions',
+ 'NBAP-PDU', Msg),
+ asn1_wrapper:decode(
'NBAP-PDU-Discriptions',
'NBAP-PDU',
- B) || _I <- lists:seq(1,N)],
- ok
+ B)
end,
- BerSMPFun =
- fun() ->
- pforeach(fun(_) ->
- [asn1_wrapper:decode(
- 'NBAP-PDU-Discriptions',
- 'NBAP-PDU',
- B) || _I <- SmpN]
- end,lists:seq(1,NumOfProcs))
- end,
-
- ?line {TimeN,ok} = timer:tc(BerFun),
- ?line {TimeNS,ok} = timer:tc(BerSMPFun),
+ nif_vs_erlang_performance({BerFun,100000,32}).
- ?line ok = testNBAPsystem:compile(Config,ber_bin,[optimize]),
+cert_pem_performance(Config) when is_list(Config) ->
+ cert_pem_performance({100000, 32});
+cert_pem_performance({N,S}) ->
+ nif_vs_erlang_performance({fun cert_pem/0,N,S}).
- ?line {TimeE,ok} = timer:tc(BerFun),
- ?line {TimeES,ok} = timer:tc(BerSMPFun),
+dsa_pem_performance(Config) when is_list(Config) ->
+ cert_pem_performance({100000, 32});
+dsa_pem_performance({N,S}) ->
+ nif_vs_erlang_performance({fun dsa_pem/0,N,S}).
+
+
+nif_vs_erlang_performance({{TC1,TC2},N,Sched}) ->
+ random:seed({123,456,789}),
+ io:format("Running a ~p sample with ~p max procs...~n~n",[N,Sched]),
+
+ {True,False} = exec(TC1,TC2,Sched,N+1),
+
+ io:format("~ndone!~n"),
+
+ io:format("~n"),TStats = print_stats(strip(True,N div 20)),
+ io:format("~n"),FStats = print_stats(strip(False,N div 20)),
+ Str = io_lib:format("~nNifs are ~.3f% faster than erlang!~n",
+ [(element(2,FStats) - element(2,TStats)) /
+ element(2,FStats) * 100]),
+ io:format(Str),
+ {comment, lists:flatten(Str)};
+nif_vs_erlang_performance({T,N,Sched}) ->
+ PTC1 = fun() ->
+ application:set_env(asn1, nif_loadable, true)
+ end,
+ PTC2 = fun() ->
+ application:set_env(asn1, nif_loadable, false)
+ end,
+ TC = fun() ->
+ element(1,timer:tc(T))
+ end,
+ nif_vs_erlang_performance({{{PTC1,TC},{PTC2,TC}},N,Sched}).
+
- ct:log("Seq:<br/>"
- "Nif : ~p (~.2f%)<br/>"
- "Erlang: ~p (~.2f%)<br/>"
- "Parallel:<br/>"
- "Nif : ~p (~.2f%)<br/>"
- "Erlang: ~p (~.2f%)<br/>",
- [TimeN,TimeN/TimeN*100,
- TimeE,TimeE/TimeN*100,
- TimeNS,TimeNS/TimeNS*100,
- TimeES,TimeES/TimeNS*100]),
+print_stats(Data) ->
+ Length = length(Data),
+ Mean = lists:sum(Data) / Length,
+ Variance = lists:foldl(fun(N,Acc) -> math:pow(N - Mean, 2)+Acc end, 0, Data),
+ StdDev = math:sqrt(Variance / Length),
+ Median = lists:nth(round(Length/2),Data),
+ Min = lists:min(Data),
+ Max = lists:max(Data),
+ if Length < 20 ->
+ io:format("Data: ~w~n",[Data]);
+ true ->
+ ok
+ end,
+ io:format("Length: ~p~nMean: ~p~nStdDev: ~p~nMedian: ~p~nMin: ~p~nMax: ~p~n",
+ [Length,Mean,StdDev,Median,Min,Max]),
+ {Length,Mean,StdDev,Median,Min,Max}.
+
+collect(Acc) ->
+ receive
+ {Tag,Val} ->
+ Prev = proplists:get_value(Tag,Acc,[]),
+ collect(lists:keystore(Tag,1,Acc,{Tag,[Val|Prev]}))
+ after 100 ->
+ Acc
+ end.
- {comment, lists:flatten(io_lib:format("Nifs are ~.2f% faster than erlang!",
- [faster(TimeN+TimeNS,
- TimeE+TimeES)]))}.
+exec(One,Two,Max,N) ->
+ exec(One,Two,Max,N,{[],[]}).
+exec(_,_,_,1,{D1,D2}) ->
+ {lists:flatten(D1),lists:flatten(D2)};
+exec({PreOne,One} = O,{PreTwo,Two} = T,MaxProcs, N, {D1,D2}) ->
+ Num = random:uniform(round(N/2)),
+ if Num rem 3 == 0 ->
+ timer:sleep(Num rem 1000);
+ true ->
+ ok
+ end,
+ Procs = random:uniform(MaxProcs),
+ io:format("\tBatch: ~p items in ~p processes, ~p left~n",[Num,Procs,N-Num]),
+ if Num rem 2 == 1 ->
+ erlang:garbage_collect(),
+ PreOne(),
+ MoreOne = pexec(One, Num, Procs, []),
+ erlang:garbage_collect(),
+ PreTwo(),
+ MoreTwo = pexec(Two, Num, Procs, []);
+ true ->
+ erlang:garbage_collect(),
+ PreTwo(),
+ MoreTwo = pexec(Two, Num, Procs, []),
+ erlang:garbage_collect(),
+ PreOne(),
+ MoreOne = pexec(One, Num, Procs, [])
+ end,
+ exec(O,T,MaxProcs,N-Num,{[MoreOne|D1],
+ [MoreTwo|D2]}).
+
+pexec(_Fun, _, 0, []) ->
+ [];
+pexec(Fun, _, 0, [{Ref,Pid}|Rest]) ->
+ receive
+ {data,D} ->
+ [D|pexec(Fun,0,0,[{Ref,Pid}|Rest])];
+ {'DOWN', Ref, process, Pid, normal} ->
+ pexec(Fun, 0,0,Rest)
+ end;
+pexec(Fun, 0, 1, AccProcs) ->
+ pexec(Fun, 0, 0, AccProcs);
+pexec(Fun, N, 1, AccProcs) ->
+ [Fun()|pexec(Fun, N - 1, 1, AccProcs)];
+pexec(Fun, N, Procs, AccProcs) ->
+ S = self(),
+ Pid = spawn(fun() ->
+ S ! {data,pexec(Fun,N,1,[])}
+ end),
+ Ref = erlang:monitor(process, Pid),
+ pexec(Fun, N, Procs - 1, [{Ref,Pid}|AccProcs]).
+strip(Data,Num) ->
+ {_,R} = lists:split(Num,lists:sort(Data)),
+ element(2,lists:split(Num,lists:reverse(R))).
faster(A,B) ->
(B - A)/B * 100.
@@ -189,3 +271,21 @@ ticket7904(Config) ->
?line {ok,_} = 'RANAPextract1':encode('InitiatingMessage', Val1),
asn1rt:unload_driver(),
?line {ok,_} = 'RANAPextract1':encode('InitiatingMessage', Val1).
+
+cert_pem() ->
+ 'OTP-PUB-KEY':decode('Certificate',<<48,130,3,184,48,130,3,33,160,3,2,1,2,2,1,1,48,13,6,9,42,134,72,134,247,13,1,1,5,5,0,48,129,131,49,14,48,12,6,3,85,4,3,19,5,111,116,112,67,65,49,19,48,17,6,3,85,4,11,19,10,69,114,108,97,110,103,32,79,84,80,49,20,48,18,6,3,85,4,10,19,11,69,114,105,99,115,115,111,110,32,65,66,49,11,48,9,6,3,85,4,6,19,2,83,69,49,18,48,16,6,3,85,4,7,19,9,83,116,111,99,107,104,111,108,109,49,37,48,35,6,9,42,134,72,134,247,13,1,9,1,22,22,112,101,116,101,114,64,101,114,105,120,46,101,114,105,99,115,115,111,110,46,115,101,48,30,23,13,48,56,48,49,48,57,48,56,50,57,51,48,90,23,13,49,55,49,49,49,55,48,56,50,57,51,48,90,48,129,132,49,15,48,13,6,3,85,4,3,19,6,99,108,105,101,110,116,49,19,48,17,6,3,85,4,11,19,10,69,114,108,97,110,103,32,79,84,80,49,20,48,18,6,3,85,4,10,19,11,69,114,105,99,115,115,111,110,32,65,66,49,11,48,9,6,3,85,4,6,19,2,83,69,49,18,48,16,6,3,85,4,7,19,9,83,116,111,99,107,104,111,108,109,49,37,48,35,6,9,42,134,72,134,247,13,1,9,1,22,22,112,101,116,101,114,64,101,114,105,120,46,101,114,105,99,115,115,111,110,46,115,101,48,129,159,48,13,6,9,42,134,72,134,247,13,1,1,1,5,0,3,129,141,0,48,129,137,2,129,129,0,245,56,68,254,220,239,193,190,63,221,182,60,67,77,121,163,214,136,137,183,139,8,166,30,100,27,45,17,126,58,15,173,151,218,75,224,148,14,22,164,10,100,186,183,104,175,197,97,96,182,146,150,106,129,140,100,194,106,90,62,133,233,155,46,155,33,101,220,83,193,182,232,240,99,253,249,114,8,159,172,143,77,179,132,229,205,29,110,185,233,224,52,25,149,249,100,80,229,199,125,23,106,146,233,159,26,13,8,161,206,221,43,240,149,42,45,194,190,85,6,235,152,220,219,160,32,144,67,2,3,1,0,1,163,130,1,55,48,130,1,51,48,9,6,3,85,29,19,4,2,48,0,48,11,6,3,85,29,15,4,4,3,2,5,224,48,29,6,3,85,29,14,4,22,4,20,26,59,44,5,72,211,158,214,23,34,30,241,125,27,123,115,93,163,231,120,48,129,179,6,3,85,29,35,4,129,171,48,129,168,128,20,6,171,128,52,58,164,184,118,178,189,157,46,40,229,109,145,222,125,1,155,161,129,140,164,129,137,48,129,134,49,17,48,15,6,3,85,4,3,19,8,101,114,108,97,110,103,67,65,49,19,48,17,6,3,85,4,11,19,10,69,114,108,97,110,103,32,79,84,80,49,20,48,18,6,3,85,4,10,19,11,69,114,105,99,115,115,111,110,32,65,66,49,18,48,16,6,3,85,4,7,19,9,83,116,111,99,107,104,111,108,109,49,11,48,9,6,3,85,4,6,19,2,83,69,49,37,48,35,6,9,42,134,72,134,247,13,1,9,1,22,22,112,101,116,101,114,64,101,114,105,120,46,101,114,105,99,115,115,111,110,46,115,101,130,1,1,48,33,6,3,85,29,17,4,26,48,24,129,22,112,101,116,101,114,64,101,114,105,120,46,101,114,105,99,115,115,111,110,46,115,101,48,33,6,3,85,29,18,4,26,48,24,129,22,112,101,116,101,114,64,101,114,105,120,46,101,114,105,99,115,115,111,110,46,115,101,48,13,6,9,42,134,72,134,247,13,1,1,5,5,0,3,129,129,0,93,11,112,227,121,15,121,179,247,135,110,216,17,197,84,18,149,166,147,142,190,178,0,209,190,0,142,233,144,100,194,205,220,182,73,204,108,42,95,23,48,63,4,120,239,42,194,25,184,35,117,107,96,229,18,45,76,122,125,40,171,210,132,50,146,178,160,55,17,35,255,208,114,30,47,55,185,154,155,165,204,180,14,143,20,234,6,234,201,225,72,235,5,87,61,255,250,23,217,1,144,246,98,221,223,102,49,168,177,13,70,241,26,27,254,251,217,14,244,18,242,197,151,50,186,214,15,42>>).
+
+dsa_pem() ->
+ 'OTP-PUB-KEY':decode('DSAPrivateKey',<<48,130,1,187,2,1,0,2,129,129,0,183,179,230,217,37,99,144,157,21,228,204,162,207,61,246,144,58,139,139,184,184,43,108,206,0,115,173,208,100,233,201,121,21,90,179,119,53,140,25,52,34,202,121,211,164,107,43,56,68,162,159,51,244,232,138,126,164,109,121,89,237,142,57,28,32,188,44,67,253,111,121,104,40,141,211,255,140,118,37,234,150,201,155,160,16,17,51,59,26,249,41,129,16,211,119,128,95,254,182,235,132,0,92,206,93,77,106,217,201,132,203,4,75,201,246,204,216,162,1,84,79,211,10,21,152,195,103,145,2,21,0,213,30,184,86,247,16,247,69,192,241,35,138,84,57,140,3,71,65,206,233,2,129,129,0,148,179,24,63,74,91,128,25,96,29,5,78,223,246,175,0,121,86,54,178,42,231,98,241,147,180,157,60,149,160,50,243,227,76,175,89,234,203,252,242,76,108,9,204,157,182,59,206,227,127,99,215,42,156,194,78,116,25,7,62,243,169,45,5,101,179,247,127,199,144,135,103,23,42,154,125,231,248,154,101,175,155,101,42,232,41,80,41,47,128,208,11,31,106,63,12,202,207,135,80,200,136,250,171,31,118,52,91,200,138,112,111,179,23,214,123,21,118,194,179,0,185,217,52,197,182,236,13,2,129,128,124,66,0,111,121,139,142,209,95,136,95,237,177,150,248,252,49,135,117,100,155,232,138,244,132,89,40,5,70,125,202,96,78,239,76,37,125,149,82,64,107,54,227,73,25,180,227,41,0,234,73,47,80,242,242,129,250,61,68,62,39,38,156,193,146,40,241,247,106,215,223,202,194,110,130,62,186,90,18,28,196,174,99,47,193,61,130,100,150,25,248,115,164,231,153,99,46,69,66,139,33,187,51,49,35,219,234,29,44,172,166,247,42,16,177,187,9,162,81,243,33,26,100,46,78,57,203,135,2,20,89,128,159,14,187,249,182,172,15,88,162,110,211,71,179,209,29,125,217,38>>),
+ 'OTP-PUB-KEY':decode('SubjectPublicKeyInfo',<<48,130,1,183,48,130,1,44,6,7,42,134,72,206,56,4,1,48,130,1,31,2,129,129,0,183,179,230,217,37,99,144,157,21,228,204,162,207,61,246,144,58,139,139,184,184,43,108,206,0,115,173,208,100,233,201,121,21,90,179,119,53,140,25,52,34,202,121,211,164,107,43,56,68,162,159,51,244,232,138,126,164,109,121,89,237,142,57,28,32,188,44,67,253,111,121,104,40,141,211,255,140,118,37,234,150,201,155,160,16,17,51,59,26,249,41,129,16,211,119,128,95,254,182,235,132,0,92,206,93,77,106,217,201,132,203,4,75,201,246,204,216,162,1,84,79,211,10,21,152,195,103,145,2,21,0,213,30,184,86,247,16,247,69,192,241,35,138,84,57,140,3,71,65,206,233,2,129,129,0,148,179,24,63,74,91,128,25,96,29,5,78,223,246,175,0,121,86,54,178,42,231,98,241,147,180,157,60,149,160,50,243,227,76,175,89,234,203,252,242,76,108,9,204,157,182,59,206,227,127,99,215,42,156,194,78,116,25,7,62,243,169,45,5,101,179,247,127,199,144,135,103,23,42,154,125,231,248,154,101,175,155,101,42,232,41,80,41,47,128,208,11,31,106,63,12,202,207,135,80,200,136,250,171,31,118,52,91,200,138,112,111,179,23,214,123,21,118,194,179,0,185,217,52,197,182,236,13,3,129,132,0,2,129,128,124,66,0,111,121,139,142,209,95,136,95,237,177,150,248,252,49,135,117,100,155,232,138,244,132,89,40,5,70,125,202,96,78,239,76,37,125,149,82,64,107,54,227,73,25,180,227,41,0,234,73,47,80,242,242,129,250,61,68,62,39,38,156,193,146,40,241,247,106,215,223,202,194,110,130,62,186,90,18,28,196,174,99,47,193,61,130,100,150,25,248,115,164,231,153,99,46,69,66,139,33,187,51,49,35,219,234,29,44,172,166,247,42,16,177,187,9,162,81,243,33,26,100,46,78,57,203,135>>),
+ 'OTP-PUB-KEY':decode('DSAParams',<<48,130,1,31,2,129,129,0,183,179,230,217,37,99,144,157,21,228,204,162,207,61,246,144,58,139,139,184,184,43,108,206,0,115,173,208,100,233,201,121,21,90,179,119,53,140,25,52,34,202,121,211,164,107,43,56,68,162,159,51,244,232,138,126,164,109,121,89,237,142,57,28,32,188,44,67,253,111,121,104,40,141,211,255,140,118,37,234,150,201,155,160,16,17,51,59,26,249,41,129,16,211,119,128,95,254,182,235,132,0,92,206,93,77,106,217,201,132,203,4,75,201,246,204,216,162,1,84,79,211,10,21,152,195,103,145,2,21,0,213,30,184,86,247,16,247,69,192,241,35,138,84,57,140,3,71,65,206,233,2,129,129,0,148,179,24,63,74,91,128,25,96,29,5,78,223,246,175,0,121,86,54,178,42,231,98,241,147,180,157,60,149,160,50,243,227,76,175,89,234,203,252,242,76,108,9,204,157,182,59,206,227,127,99,215,42,156,194,78,116,25,7,62,243,169,45,5,101,179,247,127,199,144,135,103,23,42,154,125,231,248,154,101,175,155,101,42,232,41,80,41,47,128,208,11,31,106,63,12,202,207,135,80,200,136,250,171,31,118,52,91,200,138,112,111,179,23,214,123,21,118,194,179,0,185,217,52,197,182,236,13>>),
+ 'OTP-PUB-KEY':decode('DSAPublicKey',<<2,129,128,124,66,0,111,121,139,142,209,95,136,95,237,177,150,248,252,49,135,117,100,155,232,138,244,132,89,40,5,70,125,202,96,78,239,76,37,125,149,82,64,107,54,227,73,25,180,227,41,0,234,73,47,80,242,242,129,250,61,68,62,39,38,156,193,146,40,241,247,106,215,223,202,194,110,130,62,186,90,18,28,196,174,99,47,193,61,130,100,150,25,248,115,164,231,153,99,46,69,66,139,33,187,51,49,35,219,234,29,44,172,166,247,42,16,177,187,9,162,81,243,33,26,100,46,78,57,203,135>>),
+ 'OTP-PUB-KEY':encode('DSAParams',{params,{'Dss-Parms',129000451850199666185842362389296595317127259539517666765336291347244303954511451744518587442120964433734460998523119938005801396466878889993179871123036311260456172022864663021425348874648247531097042575063545128239655736096045972718934778583429973433661785691086624069991876932064334822608460064613803976593,1216700114794736143432235288305776850295620488937,104420402274523493329542694749036577763086597934731674202966304958550599470165597750883637440049774107540742087494301536297571301945349213110548764383811017178451900599240379681904765817950545426764751538502808499880604633364255316249231153053427235538288687666086821781456733226598288985591031656134573747213}}),
+ 'OTP-PUB-KEY':encode(
+ 'SubjectPublicKeyInfo',
+ {'SubjectPublicKeyInfo',
+ {'AlgorithmIdentifier',
+ {1,2,840,10040,4,1},
+ <<48,130,1,31,2,129,129,0,183,179,230,217,37,99,144,157,21,228,204,162,207,61,246,144,58,139,139,184,184,43,108,206,0,115,173,208,100,233,201,121,21,90,179,119,53,140,25,52,34,202,121,211,164,107,43,56,68,162,159,51,244,232,138,126,164,109,121,89,237,142,57,28,32,188,44,67,253,111,121,104,40,141,211,255,140,118,37,234,150,201,155,160,16,17,51,59,26,249,41,129,16,211,119,128,95,254,182,235,132,0,92,206,93,77,106,217,201,132,203,4,75,201,246,204,216,162,1,84,79,211,10,21,152,195,103,145,2,21,0,213,30,184,86,247,16,247,69,192,241,35,138,84,57,140,3,71,65,206,233,2,129,129,0,148,179,24,63,74,91,128,25,96,29,5,78,223,246,175,0,121,86,54,178,42,231,98,241,147,180,157,60,149,160,50,243,227,76,175,89,234,203,252,242,76,108,9,204,157,182,59,206,227,127,99,215,42,156,194,78,116,25,7,62,243,169,45,5,101,179,247,127,199,144,135,103,23,42,154,125,231,248,154,101,175,155,101,42,232,41,80,41,47,128,208,11,31,106,63,12,202,207,135,80,200,136,250,171,31,118,52,91,200,138,112,111,179,23,214,123,21,118,194,179,0,185,217,52,197,182,236,13>>},
+ {0,
+ <<2,129,128,124,66,0,111,121,139,142,209,95,136,95,237,177,150,248,252,49,135,117,100,155,232,138,244,132,89,40,5,70,125,202,96,78,239,76,37,125,149,82,64,107,54,227,73,25,180,227,41,0,234,73,47,80,242,242,129,250,61,68,62,39,38,156,193,146,40,241,247,106,215,223,202,194,110,130,62,186,90,18,28,196,174,99,47,193,61,130,100,150,25,248,115,164,231,153,99,46,69,66,139,33,187,51,49,35,219,234,29,44,172,166,247,42,16,177,187,9,162,81,243,33,26,100,46,78,57,203,135>>}}).