diff options
Diffstat (limited to 'lib/asn1')
100 files changed, 4394 insertions, 3487 deletions
diff --git a/lib/asn1/Makefile b/lib/asn1/Makefile index 9c1b19605c..18e95a2471 100644 --- a/lib/asn1/Makefile +++ b/lib/asn1/Makefile @@ -26,6 +26,9 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk SUB_DIRECTORIES = src doc/src c_src +static_lib: SUB_DIRECTORIES = c_src + + include vsn.mk VSN = $(ASN1_VSN) @@ -60,7 +63,6 @@ info: version: @echo "$(VSN)" - # ---------------------------------------------------- # Application (source) release targets # ---------------------------------------------------- diff --git a/lib/asn1/c_src/Makefile b/lib/asn1/c_src/Makefile index 70238335c4..a7cd03f516 100644 --- a/lib/asn1/c_src/Makefile +++ b/lib/asn1/c_src/Makefile @@ -46,12 +46,11 @@ else TYPEMARKER = endif -EI_LIBDIR = $(ERL_TOP)/lib/erl_interface/obj$(TYPEMARKER)/$(TARGET) - # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- CFLAGS = $(DED_INCLUDES) $(EI_INCLUDES) $(DED_CFLAGS) +STATIC_CFLAGS = $(DED_INCLUDES) $(EI_INCLUDES) $(DED_STATIC_CFLAGS) LDFLAGS += $(DED_LDFLAGS) # ---------------------------------------------------- @@ -59,28 +58,55 @@ LDFLAGS += $(DED_LDFLAGS) # ---------------------------------------------------- NIF_OBJ_FILES = $(OBJDIR)/asn1_erl_nif.o +NIF_STATIC_OBJ_FILES = $(OBJDIR)/asn1_erl_nif_static.o - +# Module and shared lib have to have same name of +# static nifs to work ifeq ($(TARGET),win32) -NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.dll +NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1rt_nif.dll +NIF_LIB_FILE = $(LIBDIR)/asn1rt_nif.lib CLIB_FLAGS = LN=cp else -NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1_erl_nif.so +NIF_SHARED_OBJ_FILE = $(LIBDIR)/asn1rt_nif.so +NIF_LIB_FILE = $(LIBDIR)/asn1rt_nif.a CLIB_FLAGS = -lc LN= ln -s endif +ifeq ($(USING_VC),yes) +AR_OUT=-out: +AR_FLAGS= +else +AR_OUT= +ifeq ($(V),0) +AR_FLAGS=rc +else +AR_FLAGS=rcv +endif +endif + +ifndef RANLIB +RANLIB=true +endif + # ---------------------------------------------------- # Targets # ---------------------------------------------------- _create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR)) +ifneq ($(findstring ose,$(TARGET)),ose) opt: $(NIF_SHARED_OBJ_FILE) +else +# Do not build dynamic files on OSE +opt: +endif debug: opt +static_lib: $(NIF_LIB_FILE) + clean: rm -f core *~ rm -f $(LIBDIR)/* @@ -96,6 +122,13 @@ docs: $(OBJDIR)/%.o: %.c $(V_CC) -c $(CFLAGS) -O3 -o $@ $< +$(OBJDIR)/%_static.o: %.c + $(V_CC) -c $(STATIC_CFLAGS) -O3 -o $@ $< + +$(NIF_LIB_FILE): $(NIF_STATIC_OBJ_FILES) + $(V_AR) $(AR_FLAGS) $(AR_OUT)$@ $(NIF_STATIC_OBJ_FILES) + $(V_RANLIB) $@ + $(NIF_SHARED_OBJ_FILE): $(NIF_OBJ_FILES) $(V_LD) $(LDFLAGS) -o $(NIF_SHARED_OBJ_FILE) $(NIF_OBJ_FILES) $(CLIB_FLAGS) $(LIBS) @@ -106,7 +139,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" +ifneq ($(findstring ose,$(TARGET)),ose) $(INSTALL_PROGRAM) $(NIF_SHARED_OBJ_FILE) "$(RELSYSDIR)/priv/lib" +endif $(INSTALL_DIR) "$(RELSYSDIR)/c_src" $(INSTALL_DATA) *.c "$(RELSYSDIR)/c_src" diff --git a/lib/asn1/c_src/asn1_erl_nif.c b/lib/asn1/c_src/asn1_erl_nif.c index b3dd312fed..53e3aa1678 100644 --- a/lib/asn1/c_src/asn1_erl_nif.c +++ b/lib/asn1/c_src/asn1_erl_nif.c @@ -57,54 +57,54 @@ #define MASK(X,M) (X & M) /* PER COMPLETE */ -int per_complete(ErlNifBinary *, unsigned char *, int); +static int per_complete(ErlNifBinary *, unsigned char *, int); -int per_insert_octets(int, unsigned char **, unsigned char **, int *); +static int per_insert_octets(int, unsigned char **, unsigned char **, int *); -int per_insert_octets_except_unused(int, unsigned char **, unsigned char **, +static int per_insert_octets_except_unused(int, unsigned char **, unsigned char **, int *, int); -int per_insert_octets_as_bits_exact_len(int, int, unsigned char **, +static int per_insert_octets_as_bits_exact_len(int, int, unsigned char **, unsigned char **, int *); -int per_insert_octets_as_bits(int, unsigned char **, unsigned char **, int *); +static int per_insert_octets_as_bits(int, unsigned char **, unsigned char **, int *); -int per_pad_bits(int, unsigned char **, int *); +static int per_pad_bits(int, unsigned char **, int *); -int per_insert_least_sign_bits(int, unsigned char, unsigned char **, int *); +static int per_insert_least_sign_bits(int, unsigned char, unsigned char **, int *); -int per_insert_most_sign_bits(int, unsigned char, unsigned char **, int *); +static int per_insert_most_sign_bits(int, unsigned char, unsigned char **, int *); -int per_insert_bits_as_bits(int, int, unsigned char **, unsigned char **, int *); +static int per_insert_bits_as_bits(int, int, unsigned char **, unsigned char **, int *); -int per_insert_octets_unaligned(int, unsigned char **, unsigned char **, int); +static int per_insert_octets_unaligned(int, unsigned char **, unsigned char **, int); -int per_realloc_memory(ErlNifBinary *, int, unsigned char **); +static int per_realloc_memory(ErlNifBinary *, int, unsigned char **); /* BER DECODE */ -int ber_decode_begin(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int, +static int ber_decode_begin(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int, unsigned int *); -int ber_decode(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int *, int); +static int ber_decode(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int *, int); -int ber_decode_tag(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int, int *); +static int ber_decode_tag(ErlNifEnv *, ERL_NIF_TERM *, unsigned char *, int, int *); -int ber_decode_value(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; -int ber_encode(ErlNifEnv *, ERL_NIF_TERM , mem_chunk_t **, unsigned int *); +static int ber_encode(ErlNifEnv *, ERL_NIF_TERM , mem_chunk_t **, unsigned 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); +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); -int ber_encode_tag(ErlNifEnv *, ERL_NIF_TERM , unsigned int , +static 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 *); +static int ber_encode_length(size_t , mem_chunk_t **, unsigned int *); /* * @@ -113,7 +113,7 @@ int ber_encode_length(size_t , mem_chunk_t **, unsigned int *); * */ -int per_complete(ErlNifBinary *out_binary, unsigned char *in_buf, +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 @@ -489,7 +489,7 @@ int per_complete(ErlNifBinary *out_binary, unsigned char *in_buf, } } -int per_realloc_memory(ErlNifBinary *binary, int amount, unsigned char **ptr) { +static int per_realloc_memory(ErlNifBinary *binary, int amount, unsigned char **ptr) { int i = *ptr - binary->data; @@ -502,7 +502,7 @@ int per_realloc_memory(ErlNifBinary *binary, int amount, unsigned char **ptr) { return ASN1_OK; } -int per_insert_most_sign_bits(int no_bits, unsigned char val, +static int per_insert_most_sign_bits(int no_bits, unsigned char val, unsigned char **output_ptr, int *unused) { unsigned char *ptr = *output_ptr; @@ -523,7 +523,7 @@ int per_insert_most_sign_bits(int no_bits, unsigned char val, return ASN1_OK; } -int per_insert_least_sign_bits(int no_bits, unsigned char val, +static 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; @@ -552,7 +552,7 @@ int per_insert_least_sign_bits(int no_bits, unsigned char val, /* per_pad_bits adds no_bits bits in the buffer that output_ptr points at. */ -int per_pad_bits(int no_bits, unsigned char **output_ptr, int *unused) { +static int per_pad_bits(int no_bits, unsigned char **output_ptr, int *unused) { unsigned char *ptr = *output_ptr; int ret = 0; @@ -575,7 +575,7 @@ int per_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 per_insert_bits_as_bits(int desired_no, int no_bytes, +static 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; @@ -615,7 +615,7 @@ int per_insert_bits_as_bits(int desired_no, int no_bytes, } /* per_insert_octets_as_bits_exact_len */ -int per_insert_octets_as_bits_exact_len(int desired_len, int in_buff_len, +static 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; @@ -653,7 +653,7 @@ int per_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 per_insert_octets_as_bits(int no_bytes, unsigned char **input_ptr, +static 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; @@ -693,7 +693,7 @@ int per_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 per_insert_octets(int no_bytes, unsigned char **input_ptr, +static 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; @@ -718,7 +718,7 @@ int per_insert_octets(int no_bytes, unsigned char **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 per_insert_octets_unaligned(int no_bytes, unsigned char **input_ptr, +static 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; @@ -742,7 +742,7 @@ int per_insert_octets_unaligned(int no_bytes, unsigned char **input_ptr, return no_bytes; } -int per_insert_octets_except_unused(int no_bytes, unsigned char **input_ptr, +static 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; @@ -835,7 +835,7 @@ int per_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 ber_decode_begin(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf, +static 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; @@ -857,7 +857,7 @@ int ber_decode_begin(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf, return ASN1_OK; } -int ber_decode(ErlNifEnv* env, ERL_NIF_TERM *term, unsigned char *in_buf, +static 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; @@ -889,7 +889,7 @@ int ber_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 ber_decode_tag(ErlNifEnv* env, ERL_NIF_TERM *tag, unsigned char *in_buf, +static 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; @@ -936,21 +936,36 @@ int ber_decode_tag(ErlNifEnv* env, ERL_NIF_TERM *tag, unsigned char *in_buf, * in_buf and puts the value part in the decode_buf as an Erlang * nif term into value */ -int ber_decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *in_buf, +static 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; unsigned int lenoflen = 0; - int indef = 0; unsigned char *tmp_out_buff; ERL_NIF_TERM term = 0, curr_head = 0; if (((in_buf[*ib_index]) & 0x80) == ASN1_SHORT_DEFINITE_LENGTH) { len = in_buf[*ib_index]; - } else if (in_buf[*ib_index] == ASN1_INDEFINITE_LENGTH - ) - indef = 1; - else /* long definite length */{ + } else if (in_buf[*ib_index] == ASN1_INDEFINITE_LENGTH) { + (*ib_index)++; + curr_head = enif_make_list(env, 0); + if (*ib_index+1 >= in_buf_len) { + return ASN1_INDEF_LEN_ERROR; + } + while (!(in_buf[*ib_index] == 0 && in_buf[*ib_index + 1] == 0)) { + maybe_ret = ber_decode(env, &term, in_buf, ib_index, in_buf_len); + if (maybe_ret <= ASN1_ERROR) { + return maybe_ret; + } + curr_head = enif_make_list_cell(env, term, curr_head); + if (*ib_index+1 >= in_buf_len) { + return ASN1_INDEF_LEN_ERROR; + } + } + enif_make_reverse_list(env, curr_head, value); + (*ib_index) += 2; /* skip the indefinite length end bytes */ + return ASN1_OK; + } else /* long definite length */{ lenoflen = (in_buf[*ib_index] & 0x7f); /*length of length */ if (lenoflen > (in_buf_len - (*ib_index + 1))) return ASN1_LEN_ERROR; @@ -965,23 +980,7 @@ int ber_decode_value(ErlNifEnv* env, ERL_NIF_TERM *value, unsigned char *in_buf, if (len > (in_buf_len - (*ib_index + 1))) return ASN1_VALUE_ERROR; (*ib_index)++; - if (indef == 1) { /* in this case it is desireably to check that indefinite length - end bytes exist in inbuffer */ - curr_head = enif_make_list(env, 0); - while (!(in_buf[*ib_index] == 0 && in_buf[*ib_index + 1] == 0)) { - if (*ib_index >= in_buf_len) - return ASN1_INDEF_LEN_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); - } - enif_make_reverse_list(env, curr_head, value); - (*ib_index) += 2; /* skip the indefinite length end bytes */ - } else if (form == ASN1_CONSTRUCTED) - { + if (form == ASN1_CONSTRUCTED) { int end_index = *ib_index + len; if (end_index > in_buf_len) return ASN1_LEN_ERROR; @@ -1012,7 +1011,7 @@ struct ber_encode_mem_chunk { char *curr; }; -int ber_encode(ErlNifEnv *env, ERL_NIF_TERM term, mem_chunk_t **curr, unsigned int *count) { +static int ber_encode(ErlNifEnv *env, ERL_NIF_TERM term, mem_chunk_t **curr, unsigned int *count) { const ERL_NIF_TERM *tv; unsigned int form; @@ -1087,7 +1086,7 @@ int ber_encode(ErlNifEnv *env, ERL_NIF_TERM term, mem_chunk_t **curr, unsigned i return ASN1_OK; } -int ber_encode_tag(ErlNifEnv *env, ERL_NIF_TERM tag, unsigned int form, +static 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)) @@ -1122,7 +1121,7 @@ int ber_encode_tag(ErlNifEnv *env, ERL_NIF_TERM tag, unsigned int form, } } -int ber_encode_length(size_t size, mem_chunk_t **curr, unsigned int *count) { +static 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; @@ -1150,7 +1149,7 @@ int ber_encode_length(size_t size, mem_chunk_t **curr, unsigned int *count) { return ASN1_OK; } -mem_chunk_t *ber_new_chunk(unsigned int length) { +static mem_chunk_t *ber_new_chunk(unsigned int length) { mem_chunk_t *new = enif_alloc(sizeof(mem_chunk_t)); if (new == NULL) return NULL; @@ -1165,7 +1164,7 @@ mem_chunk_t *ber_new_chunk(unsigned int length) { return new; } -void ber_free_chunks(mem_chunk_t *chunk) { +static void ber_free_chunks(mem_chunk_t *chunk) { mem_chunk_t *curr, *next = chunk; while (next != NULL) { curr = next; @@ -1175,7 +1174,7 @@ void ber_free_chunks(mem_chunk_t *chunk) { } } -int ber_check_memory(mem_chunk_t **curr, unsigned int needed) { +static int ber_check_memory(mem_chunk_t **curr, unsigned int needed) { mem_chunk_t *new; if ((*curr)->curr-needed >= (*curr)->top) return ASN1_OK; @@ -1320,6 +1319,7 @@ static void unload(ErlNifEnv* env, void* priv_data) { } + static ErlNifFunc nif_funcs[] = { { "encode_per_complete", 1, encode_per_complete }, { "decode_ber_tlv_raw", 1, decode_ber_tlv_raw }, diff --git a/lib/asn1/doc/src/asn1_spec.xmlsrc b/lib/asn1/doc/src/asn1_spec.xmlsrc index 07cba17816..9001aca65c 100644 --- a/lib/asn1/doc/src/asn1_spec.xmlsrc +++ b/lib/asn1/doc/src/asn1_spec.xmlsrc @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="iso-8859-1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml index 362ca9330f..8b33497dd3 100644 --- a/lib/asn1/doc/src/asn1_ug.xml +++ b/lib/asn1/doc/src/asn1_ug.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="iso-8859-1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -34,23 +34,25 @@ <section> <title>Features</title> - <p>The Asn1 application provides: - </p> + <p>The Asn1 application provides:</p> <list type="bulleted"> <item>An ASN.1 compiler for Erlang, which generates encode and decode functions to be used by Erlang programs sending and receiving ASN.1 specified data.</item> <item>Run-time functions used by the generated code.</item> - <item>The supported encoding rules are: + <item>Support for the following encoding rules: <list> <item> Basic Encoding Rules (<em>BER</em>) </item> <item> - Distinguished Encoding Rules (<em>DER</em>), a specialized form of BER that is used in security-conscious applications. + Distinguished Encoding Rules (<em>DER</em>), a specialized + form of BER that is used in security-conscious + applications. </item> <item> - Packed Encoding Rules (<em>PER</em>) both the aligned and unaligned variant. + Packed Encoding Rules (<em>PER</em>); both the aligned and + unaligned variant. </item> </list> </item> @@ -59,71 +61,41 @@ <section> <title>Overview</title> - <p>ASN.1 (Abstract Syntax Notation 1) is a formal language for describing data structures to be exchanged between distributed computer systems. - The purpose of ASN.1 is to have - a platform and programming language independent notation to express - types using a - standardized set of rules for the transformation of values of - a defined type, into a stream of bytes. This stream of bytes - can then be sent on a communication channel set up by the - lower layers in the stack of communication protocols e.g. - TCP/IP or encapsulated within UDP packets. This way, two - different applications written in two completely different - programming languages running on different computers with - different internal representation of data can exchange - instances of structured data types (instead of exchanging - bytes or bits). This makes programming faster and easier since no code - has to be written to process the transport format of the - data. - </p> - <p>To write a network application which processes ASN.1 encoded - messages, it is prudent and sometimes essential to have a set - of off-line development tools such as an ASN.1 compiler which - can generate the encode and decode logic for the specific ASN.1 - data types. It is also necessary to combine this with some - general language-specific runtime support for ASN.1 encoding and - decoding. - </p> - <p>The ASN.1 compiler must be directed towards a target language - or a set of closely related languages. This manual describes a - compiler which is directed towards the functional language - Erlang. In order to use this compiler, familiarity with the - language Erlang is essential. Therefore, the runtime support for ASN.1 is - also closely related to the language Erlang and - consist of a number of functions, which the - compiler uses. The types in ASN.1 and how to represent - values of those types in Erlang are described in this manual. - </p> - <p>The following document is structured so that the first part describes - how to use ASN.1 compiler, and then there are descriptions of all - the primitive and constructed ASN.1 types and their representation - in Erlang, - </p> + <p>ASN.1 (Abstract Syntax Notation One) is a formal language for + describing data structures to be exchanged between distributed + computer systems. The purpose of ASN.1 is to have a platform + and programming language independent notation to express types + using a standardized set of rules for the transformation of + values of a defined type into a stream of bytes. This stream of + bytes can then be sent on any type of communication + channel. This way, two applications written in different + programming languages running on different computers with + different internal representation of data can exchange instances + of structured data types.</p> </section> <section> <title>Prerequisites</title> - <p>It is assumed that the reader is familiar with the ASN.1 notation - as documented in the standard definition [<cite id="X.680"></cite>] which is - the primary text. It may also be helpful, but not necessary, - to read the standard definitions - [<cite id="X.681"></cite>] [<cite id="X.682"></cite>] [<cite id="X.683"></cite>] - [<cite id="X.690"></cite>] [<cite id="X.691"></cite>]. </p> - <p>A very good book explaining those reference texts is - [<cite id="DUBUISSON"></cite>], free to download at - <url href="http://www.oss.com/asn1/dubuisson.html">http://www.oss.com/asn1/dubuisson.html </url>. + <p>It is assumed that the reader is familiar with the ASN.1 + notation as documented in the standard definition [<cite + id="X.680"></cite>] which is the primary text. It may also be + helpful, but not necessary, to read the standard definitions + [<cite id="X.681"></cite>] [<cite id="X.682"></cite>] [<cite + id="X.683"></cite>] [<cite id="X.690"></cite>] [<cite + id="X.691"></cite>]. </p> + <p>A good book explaining those reference texts is + [<cite id="DUBUISSON"></cite>], which is free to download at + <url href="http://www.oss.com/asn1/dubuisson.html">http://www.oss.com/asn1/dubuisson.html</url>. </p> </section> <section> - <title>Capability</title> + <title>Capabilities</title> <p>This application covers all features of ASN.1 up to the 1997 - edition of the specification. In the 2002 edition of ASN.1 a number of - new features where introduced of which some are supported while - others are not. For example the - ECN (Encoding Control Notation) and XML notation are still - unsupported. Though, the other features of the 2002 edition are - fully or partly supported as shown below:</p> + edition of the specification. In the 2002 edition of ASN.1 a + number of new features were introduced. The following features + of the 2002 edition are fully or partly supported as shown + below:</p> <list type="bulleted"> <item> <p>Decimal notation (e.g., "1.5e3") for REAL values. The @@ -131,7 +103,7 @@ supported.</p> </item> <item> - <p>The RELATIVE-OID type for relative object identifiers are + <p>The RELATIVE-OID type for relative object identifiers is fully supported.</p> </item> <item> @@ -141,16 +113,16 @@ constraint is not a PER-visible constraint.</p> </item> <item> - <p>The subtype constraint by regular expressions (PATTERN) for character string types is parsed when compiling, but no further action is taken. This constraint is not a PER-visible constraint.</p> + <p>The subtype constraint by regular expressions (PATTERN) + for character string types is parsed when compiling, but no + further action is taken. This constraint is not a + PER-visible constraint.</p> </item> <item> <p>Multiple-line comments as in C, <c>/* ... */</c>, are supported.</p> </item> </list> - <p>It should also be added here that the encoding formats - supported are <em>BER</em>, <em>DER</em>, <em>PER aligned - basic</em> variant and <em>PER unaligned basic</em> variant.</p> </section> </section> @@ -162,19 +134,17 @@ <title>A First Example</title> <p>The following example demonstrates the basic functionality used to run the Erlang ASN.1 compiler.</p> - <p>First, create a file called <c>People.asn</c> containing the following:</p> + <p>Create a file called <c>People.asn</c> containing the following:</p> <pre> -People DEFINITIONS IMPLICIT TAGS ::= - +People DEFINITIONS AUTOMATIC TAGS ::= BEGIN -EXPORTS Person; - -Person ::= [PRIVATE 19] SEQUENCE { - name PrintableString, - location INTEGER {home(0),field(1),roving(2)}, - age INTEGER OPTIONAL } + Person ::= SEQUENCE { + name PrintableString, + location INTEGER {home(0),field(1),roving(2)}, + age INTEGER OPTIONAL + } END </pre> - <p>This file (<c>people.asn</c>) must be compiled before it can be + <p>This file (<c>People.asn</c>) must be compiled before it can be used. The ASN.1 compiler checks that the syntax is correct and that the text represents proper ASN.1 code before generating an abstract @@ -186,14 +156,14 @@ END </pre> The following shows how the compiler can be called from the Erlang shell:</p> <pre> -1><input>asn1ct:compile("People", [ber]).</input> +1><input> asn1ct:compile("People", [ber]).</input> ok 2> </pre> <p>The <c>verbose</c> option can be given to have information about the generated files printed:</p> <pre> -2><input>asn1ct:compile("People", [ber,verbose]).</input> +2><input> asn1ct:compile("People", [ber,verbose]).</input> Erlang ASN.1 compiling "People.asn" --{generated,"People.asn1db"}-- --{generated,"People.hrl"}-- @@ -201,20 +171,17 @@ Erlang ASN.1 compiling "People.asn" ok 3> </pre> - <p>The ASN.1 module People is now accepted and the abstract syntax tree - is saved in the <c>People.asn1db</c> file, the - generated Erlang code is compiled using the Erlang compiler and - loaded into the Erlang runtime system. Now there is a user interface - of encode/2 and decode/2 in the module People, which is invoked by: - <br></br> -<c><![CDATA['People':encode(<Type name>,<Value>),]]></c> <br></br> - + <p>The ASN.1 module <c>People</c> is now accepted and the + abstract syntax tree is saved in the <c>People.asn1db</c> file; + the generated Erlang code is compiled using the Erlang compiler + and loaded into the Erlang run-time system. Now there is an API + for <c>encode/2</c> and <c>decode/2</c> in the module + <c>People</c>, which is invoked by: <br></br> + <c><![CDATA['People':encode(<Type name>, <Value>)]]></c> + <br></br> or <br></br> -<c><![CDATA['People':decode(<Type name>,<Value>),]]></c> <br></br> +<c><![CDATA['People':decode(<Type name>, <Value>)]]></c></p> - Alternatively one can use the <c><![CDATA[asn1rt:encode(<Module name> ,<Type name>,<Value>)]]></c> and <c><![CDATA[asn1rt:decode(< Module name>,<Type name>,<Value>)]]></c> calls. - However, they are not as efficient as the previous methods since they - result in an additional <c>apply/3</c> call.</p> <p>Assume there is a network application which receives instances of the ASN.1 defined type Person, modifies and sends them back again:</p> @@ -237,33 +204,30 @@ receive constructed and encoded using <c>'People':encode('Person',Answer)</c> which takes an instance of a defined ASN.1 type and transforms it to a - binary according to the BER or PER - encoding-rules. + binary according to the BER or PER encoding rules. <br></br> The encoder and the decoder can also be run from - the shell. The following dialogue with the shell illustrates - how the functions - <c>asn1rt:encode/3</c> and <c>asn1rt:decode/3</c> are used.</p> + the shell.</p> <pre> 2> <input>Rockstar = {'Person',"Some Name",roving,50}.</input> {'Person',"Some Name",roving,50} -3> <input>{ok,Bin} = asn1rt:encode('People','Person',Rockstar).</input> +3> <input>{ok,Bin} = 'People':encode('Person',Rockstar).</input> {ok,<<243,17,19,9,83,111,109,101,32,78,97,109,101,2,1,2, 2,1,50>>} -4> <input>{ok,Person} = asn1rt:decode('People','Person',Bin).</input> +4> <input>{ok,Person} = 'People':decode('Person',Bin).</input> {ok,{'Person',"Some Name",roving,50}} 5> </pre> </section> <section> <title>Module dependencies</title> - <p>It is common that asn1 modules import defined types, values and - other entities from another asn1 module.</p> - <p>Earlier versions of the asn1 compiler required that modules that + <p>It is common that ASN.1 modules import defined types, values and + other entities from another ASN.1 module.</p> + <p>Earlier versions of the ASN.1 compiler required that modules that were imported from had to be compiled before the module that - imported. This caused problems when asn1 modules had circular + imported. This caused problems when ASN.1 modules had circular dependencies.</p> - <p>Now are referenced modules parsed when the compiler finds an + <p>Referenced modules are now parsed when the compiler finds an entity that is imported. There will not be any code generated for the referenced module. However, the compiled module rely on that the referenced modules also will be compiled.</p> @@ -279,11 +243,8 @@ The encoder and the decoder can also be run from (including the compiler).</p> </item> <item> - <p>The module <c>asn1rt</c> which provides the run-time functions. - However, it is preferable to use the generated <c>encode/2</c> and - <c>decode/2</c> functions in each module, ie. - Module:encode(Type,Value), in favor of the <c>asn1rt</c> - interface.</p> + <p>The module <c>asn1rt_nif</c> which provides the run-time functions + for the ASN.1 decoder for the BER back-end.</p> </item> </list> <p>The reason for the division of the interface into compile-time @@ -318,7 +279,7 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn </item> <tag><c>-I IncludeDir</c></tag> <item> - <p>Where to search for <c>.asn1db</c> files and asn1 + <p>Where to search for <c>.asn1db</c> files and ASN.1 source specs in order to resolve references to other modules. This option can be repeated many times if there are several places to search in. The compiler will always @@ -330,26 +291,26 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn </item> <tag><c>+asn1config</c></tag> <item> - <p>This functionality works together with the flags - <c>ber</c>. It enables the + <p>This functionality works together with the + <c>ber</c> option. It enables the specialized decodes, see the <seealso marker="asn1_spec">Specialized Decode</seealso> chapter. </p> </item> <tag><c>+undec_rest</c></tag> <item> - <p>A buffer that holds a message, being decoded may - also have some following bytes. Now it is possible to get - those following bytes returned together with the decoded - value. If an asn1 spec is compiled with this option a tuple - <c>{ok,Value,Rest}</c> is returned. <c>Rest</c> may be a - list or a binary. Earlier versions of the compiler ignored - those following bytes.</p> + <p>A buffer that holds a message being decoded may also have + trailing bytes. If those trailing bytes are important they + can be returned along with the decoded value by compiling + the ASN.1 specification with the <c>+undec_rest</c> option. + The return value from the decoder will be + <c>{ok,Value,Rest}</c> where <c>Rest</c> is a binary + containing the trailing bytes.</p> </item> <tag><c>+'Any Erlc Option'</c></tag> <item> <p>You may add any option to the Erlang compiler when compiling the generated Erlang files. Any option - unrecognised by the asn1 compiler will be passed to the + unrecognized by the ASN.1 compiler will be passed to the Erlang compiler.</p> </item> </taglist> @@ -374,35 +335,15 @@ asn1ct:compile("H323-MESSAGES.asn1",[ber]). </pre> asn1ct:compile("H323-MESSAGES.asn1",[per]). </pre> <p>The generic encode and decode functions can be invoked like this:</p> <pre> -asn1ct:encode('H323-MESSAGES','SomeChoiceType',{call,"octetstring"}). -asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre> - <p>Or, preferable like:</p> - <pre> 'H323-MESSAGES':encode('SomeChoiceType',{call,"octetstring"}). 'H323-MESSAGES':decode('SomeChoiceType',Bytes). </pre> </section> <section> <title>Run-time Functions</title> - <p>A brief description of the major functions is given here. For a - complete description of each function see - <seealso marker="asn1rt"> the Asn1 Reference Manual</seealso>, the <c>asn1rt</c> module.</p> - <p>The generic run-time encode and decode functions can be invoked as below:</p> - <pre> -asn1rt:encode('H323-MESSAGES','SomeChoiceType',{call,"octetstring"}). -asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre> - <p>Or, preferable like:</p> - <pre> -'H323-MESSAGES':encode('SomeChoiceType',{call,"octetstring"}). -'H323-MESSAGES':decode('SomeChoiceType',Bytes). </pre> - <p>The asn1 nif is enabled in two occasions: encoding of - asn1 values when the asn1 spec is compiled with <c>per</c> and - or decode of encoded asn1 values when the asn1 spec is - compiled with <c>ber</c>. In - those cases the nif will be loaded automatically at the first call - to <c>encode</c>/<c>decode</c>. If one doesn't want the performance - overhead of the nif being loaded at the first call it is possible - to load the nif separately by loading the <c>asn1rt_nif</c> module.</p> + <p>When an ASN.1 specification is compiled with the <c>ber</c> + option, the module <c>asn1rt_nif</c> module and the NIF library in + <c>asn1/priv_dir</c> will be needed at run-time.</p> <p>By invoking the function <c>info/0</c> in a generated module, one gets information about which compiler options were used.</p> </section> @@ -413,9 +354,9 @@ asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre> compile time appear on the screen together with a line number indicating where in the source file the error was detected. If no errors are found, an Erlang ASN.1 module will - be created as default.</p> - <p>The run-time encoders and decoders (in the <c>asn1rt</c> module) do - execute within a catch and returns <c>{ok, Data}</c> or + be created.</p> + <p>The run-time encoders and decoders execute within a catch and + returns <c>{ok, Data}</c> or <c>{error, {asn1, Description}}</c> where <c>Description</c> is an Erlang term describing the error. </p> @@ -424,18 +365,18 @@ asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre> <section> <marker id="inlineExamples"></marker> - <title>Multi File Compilation</title> - <p>There are various reasons for using a multi file compilation:</p> + <title>Multi-file Compilation</title> + <p>There are various reasons for using multi-file compilation:</p> <list type="bulleted"> - <item>You want to choose name for the generated module by - any reason. Maybe you need to compile the same specs for - different encoding/decoding standards.</item> + <item>You want to choose the name for the generated module, + perhaps because you need to compile the same specs for + different encoding rules.</item> <item>You want only one resulting module.</item> </list> - <p>You need to specify which asn1 specs you will + <p>You need to specify which ASN.1 specs you will compile in a module that must have the extension <c>.set.asn</c>. You chose name of the module and provide the - names of the asn1 specs. For instance, if you have the specs + names of the ASN.1 specs. For instance, if you have the specs <c>File1.asn</c>, <c>File2.asn</c> and <c>File3.asn</c> your module <c>MyModule.set.asn</c> will look like:</p> <pre> @@ -446,11 +387,45 @@ File3.asn </pre> <code type="none"> ~> erlc MyModule.set.asn </code> <p>the result will be one merged module <c>MyModule.erl</c> with - the generated code from the three asn1 specs. + the generated code from the three ASN.1 specs. </p> </section> <section> + <title>A quick note about tags</title> + + <p>Tags used to be important for all users of ASN.1, because it + was necessary to manually add tags to certain constructs in order + for the ASN.1 specification to be valid. Here is an example of + an old-style specification:</p> + + <pre> +Tags DEFINITIONS ::= +BEGIN + Afters ::= CHOICE { cheese [0] IA5String, + dessert [1] IA5String } +END </pre> + + <p>Without the tags (the numbers in square brackets) the ASN.1 + compiler would refuse to compile the file.</p> + + <p>In 1994 the global tagging mode AUTOMATIC TAGS was introduced. + By putting AUTOMATIC TAGS in the module header, the ASN.1 compiler + will automatically add tags when needed. Here is the same + specification in AUTOMATIC TAGS mode:</p> + + <pre> +Tags DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + Afters ::= CHOICE { cheese IA5String, + dessert IA5String } +END +</pre> + + <p>Tags will not be mentioned any more in this manual.</p> + </section> + + <section> <marker id="ASN1Types"></marker> <title>The ASN.1 Types</title> <p>This section describes the ASN.1 types including their @@ -521,7 +496,7 @@ Operational ::= BOOLEAN --ASN.1 definition </pre> <p>In Erlang code it may look like:</p> <pre> Val = true, -{ok,Bytes}=asn1rt:encode(MyModule,'Operational',Val), </pre> +{ok,Bytes} = MyModule:encode('Operational', Val), </pre> <p>Below follows a description of how values of each type can be represented in Erlang. </p> @@ -587,20 +562,18 @@ T6value3 = white <section> <marker id="REAL"></marker> <title>REAL</title> - <p>In this version reals are not implemented. When they are, - the following - ASN.1 type is used:</p> + <p>The following ASN.1 type is used for real numbers:</p> <pre> R1 ::= REAL </pre> - <p>Can be assigned a value in Erlang as:</p> + <p>It can be assigned a value in Erlang as:</p> <pre> -R1value1 = 2.14, +R1value1 = "2.14", R1value2 = {256,10,-2}, </pre> <p>In the last line note that the tuple {256,10,-2} is the real number 2.56 in a special notation, which will encode faster than simply - stating the number as 2.56. The arity three tuple is + stating the number as <c>"2.56"</c>. The arity three tuple is <c>{Mantissa,Base,Exponent}</c> i.e. Mantissa * Base^Exponent.</p> </section> @@ -653,7 +626,7 @@ Day1 = saturday, Bits1 ::= BIT STRING Bits2 ::= BIT STRING {foo(0),bar(1),gnu(2),gnome(3),punk(14)} </pre> - <p>There are five different notations available for representation of + <p>There are two notations available for representation of BIT STRING values in Erlang and as input to the encode functions.</p> <list type="ordered"> <item>A bitstring. By default, a BIT STRING with no @@ -661,43 +634,10 @@ Bits2 ::= BIT STRING {foo(0),bar(1),gnu(2),gnome(3),punk(14)} <item>A list of atoms corresponding to atoms in the <c>NamedBitList</c> in the BIT STRING definition. A BIT STRING with symbolic names will always be decoded to this format.</item> - <item>A list of binary digits (0 or 1). This format is always - accepted as input to the encode functions. A BIT STRING will - be decoded to this format if <em>legacy_bit_string</em> option - has been given. <em>This format may be withdrawn in a future - release.</em> - </item> - <item>As <c>{Unused,Binary}</c> where <c>Unused</c> denotes how - many trailing zero-bits 0 to 7 that are unused in the least - significant byte in <c>Binary</c>. This format is always - accepted as input to the encode functions. A BIT STRING will - be decoded to this format if <em>compact_bit_string</em> has - been given. <em>This format may be withdrawn in a future - release.</em> - </item> - <item>A hexadecimal number (or an integer). This format should be - avoided, since it is easy to misinterpret a <c>BIT STRING</c> - value in this format. <em>This format may be withdrawn in a future - release.</em> - </item> </list> - <note> - <p>It is recommended to either use the bitstring format (for - BIT STRINGs with no symbolic names) or a list of symbolic - names (for BIT STRINGs with symbolic names). The other formats - should be avoided since they may be withdrawn in a future - release. - </p> - </note> + <p>Example:</p> <pre> Bits1Val1 = <<0:1,1:1,0:1,1:1,1:1>>, -Bits1Val2 = 16#1A, -Bits1Val3 = {3,<<0:1,1:1,0:1,1:1,1:1,0:3>>}, -Bits1Val4 = [0,1,0,1,1] - </pre> - <p>Note that <c>Bits1Val1</c>, <c>Bits1Val2</c>, <c>Bits1Val3</c>, - and <c>Bits1Val1</c> denote the same value.</p> - <pre> Bits2Val1 = [gnu,punk], Bits2Val2 = <<2#1110:4>>, Bits2Val3 = [bar,gnu,gnome], @@ -708,37 +648,60 @@ Bits2Val3 = [bar,gnu,gnome], 2 and 14 are set to 1 and the rest set to 0. The symbolic values appear as a list of values. If a named value appears, which is not specified in the type definition, a run-time error will occur.</p> - <p>The compact notation equivalent to the empty BIT STRING is - <c><![CDATA[{0,<<>>}]]></c>, which in the other notations is - <c><![CDATA[<<>>]]></c>, <c>[]</c>, or - <c>0</c>.</p> <p>BIT STRINGS may also be sub-typed with, for example, a SIZE specification:</p> <pre> Bits3 ::= BIT STRING (SIZE(0..31)) </pre> <p>This means that no bit higher than 31 can ever be set.</p> + + <section> + <title>Deprecated representations for BIT STRING</title> + <p>In addition to the representations described above, the + following deprecated representations are available if the + specification has been compiled with the + <c>legacy_erlang_types</c> option:</p> + <list type="ordered"> + <item>A list of binary digits (0 or 1). This format is + accepted as input to the encode functions, and a BIT STRING + will be decoded to this format if the + <em>legacy_bit_string</em> option has been given. + </item> + <item>As <c>{Unused,Binary}</c> where <c>Unused</c> denotes + how many trailing zero-bits 0 to 7 that are unused in the + least significant byte in <c>Binary</c>. This format is + accepted as input to the encode functions, and a <c>BIT + STRING</c> will be decoded to this format if + <em>compact_bit_string</em> has been given. + </item> + <item>A hexadecimal number (or an integer). This format + should be avoided, since it is easy to misinterpret a BIT + STRING value in this format. + </item> + </list> + </section> </section> <section> <marker id="OCTET STRING"></marker> <title>OCTET STRING</title> - <p>The OCTET STRING is the simplest of all ASN.1 types The OCTET STRING - only moves or transfers e.g. binary files or other unstructured - information complying to two rules. - Firstly, the bytes consist of octets and secondly, encoding is - not required.</p> + <p>The OCTET STRING is the simplest of all ASN.1 types. The + OCTET STRING only moves or transfers e.g. binary files or other + unstructured information complying to two rules. Firstly, the + bytes consist of octets and secondly, encoding is not + required.</p> <p>It is possible to have the following ASN.1 type definitions:</p> <pre> O1 ::= OCTET STRING O2 ::= OCTET STRING (SIZE(28)) </pre> <p>With the following example assignments in Erlang:</p> <pre> -O1Val = [17,13,19,20,0,0,255,254], -O2Val = "must be exactly 28 chars....", </pre> - <p>Observe that <c>O1Val</c> is assigned a series of numbers between 0 - and 255 i.e. octets. - <c>O2Val</c> is assigned using the string notation. - </p> +O1Val = <<17,13,19,20,0,0,255,254>>, +O2Val = <<"must be exactly 28 chars....">>,</pre> + <p>By default, an OCTET STRING is always represented as + an Erlang binary. If the specification has been compiled with + the <c>legacy_erlang_types</c> option, the encode functions + will accept both lists and binaries, and the decode functions + will decode an OCTET STRING to a list.</p> </section> <section> @@ -770,13 +733,11 @@ O2Val = "must be exactly 28 chars....", </pre> specified for a type are especially important for PER, where they affect the encoding. </p> - <p>Please note that <em>all</em> the Character strings are - supported and it is possible to use the following ASN.1 type - definitions:</p> + <p>Here are some examples:</p> <pre> Digs ::= NumericString (SIZE(1..3)) TextFile ::= IA5String (SIZE(0..64000)) </pre> - <p>and the following Erlang assignments:</p> + <p>with corresponding Erlang assignments:</p> <pre> DigsVal1 = "456", DigsVal2 = "123", @@ -789,70 +750,86 @@ TextFileVal2 = [88,76,55,44,99,121 .......... a lot of characters here ....] characters are all represented by quadruples beginning with three zeros like {0,0,0,65} for the 'A' character. When decoding a value for these strings the result is a list of - quadruples, or integers when the value is an ASCII character. - The following example shows how it works:</p> - <p>In a file <c>PrimStrings.asn1</c> the type <c>BMP</c> is defined as - <br></br> -<c>BMP ::= BMPString</c> then using BER encoding (<c>ber</c> - option)the input/output format will be:</p> + quadruples, or integers when the value is an ASCII character.</p> + + <p>The following example shows how it works. We have the following + specification in the file <c>PrimStrings.asn1</c>.</p> + <pre> +PrimStrings DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + BMP ::= BMPString +END + </pre> + + <p>Encoding and decoding some strings:</p> + <pre> -1> <input>{ok,Bytes1} = asn1rt:encode('PrimStrings','BMP',[{0,0,53,53},{0,0,45,56}]).</input> -{ok,[30,4,"55-8"]} -2> <input>asn1rt:decode('PrimStrings','BMP',list_to_binary(Bytes1)).</input> +1> <input>asn1ct:compile('PrimStrings', [ber]).</input> +ok +2> <input>{ok,Bytes1} = 'PrimStrings':encode('BMP', [{0,0,53,53},{0,0,45,56}]).</input> +{ok,<<30,4,53,54,45,56>>} +3> <input>'PrimStrings':decode('BMP', Bytes1).</input> {ok,[{0,0,53,53},{0,0,45,56}]} -3> <input>{ok,Bytes2} = asn1rt:encode('PrimStrings','BMP',[{0,0,53,53},{0,0,0,65}]).</input> -{ok,[30,4,[53,53,0,65]]} -4> <input>asn1rt:decode('PrimStrings','BMP',list_to_binary(Bytes2)).</input> +4> <input>{ok,Bytes2} = 'PrimStrings':encode('BMP', [{0,0,53,53},{0,0,0,65}]).</input> +{ok,<<30,4,53,53,0,65>>} +5> <input>'PrimStrings':decode('BMP', Bytes2).</input> {ok,[{0,0,53,53},65]} -5> <input>{ok,Bytes3} = asn1rt:encode('PrimStrings','BMP',"BMP string").</input> -{ok,[30,20,[0,66,0,77,0,80,0,32,0,115,0,116,0,114,0,105,0,110,0,103]]} -6> <input>asn1rt:decode('PrimStrings','BMP',list_to_binary(Bytes3)).</input> +6> <input>{ok,Bytes3} = 'PrimStrings':encode('BMP', "BMP string").</input> +{ok,<<30,20,0,66,0,77,0,80,0,32,0,115,0,116,0,114,0,105,0,110,0,103>>} +7> <input>'PrimStrings':decode('BMP', Bytes3).</input> {ok,"BMP string"} </pre> - <p>The UTF8String is represented in Erlang as a list of integers, - where each integer represents the unicode value of one - character. When a value shall be encoded one first has to - transform it to a UTF8 encoded binary, then it can be encoded by - asn1. When decoding the result is a UTF8 encoded binary, which - may be transformed to an integer list. The transformation - functions, <c>utf8_binary_to_list</c> and - <c>utf8_list_to_binary</c>, are in the <c>asn1rt</c> module. In - the example below we assume an asn1 definition <c>UTF ::= UTF8String</c> in a module <c>UTF.asn</c>:</p> + + <p>The UTF8String type is represented as a UTF-8 encoded binary in + Erlang. Such binaries can be created directly using the binary syntax + or by converting from a list of Unicode code points using the + <c>unicode:characters_to_binary/1</c> function.</p> + + <p>Here are some examples showing how UTF-8 encoded binaries can + be created and manipulated:</p> + + <pre> +1> <input>Gs = "Мой маленький Гном".</input> +[1052,1086,1081,32,1084,1072,1083,1077,1085,1100,1082,1080, + 1081,32,1043,1085,1086,1084] +2> <input>Gbin = unicode:characters_to_binary(Gs).</input> +<<208,156,208,190,208,185,32,208,188,208,176,208,187,208, + 181,208,189,209,140,208,186,208,184,208,185,32,208,147, + 208,...>> +3> <input>Gbin = <<"Мой маленький Гном"/utf8>>.</input> +<<208,156,208,190,208,185,32,208,188,208,176,208,187,208, + 181,208,189,209,140,208,186,208,184,208,185,32,208,147, + 208,...>> +4> <input>Gs = unicode:characters_to_list(Gbin).</input> +[1052,1086,1081,32,1084,1072,1083,1077,1085,1100,1082,1080, + 1081,32,1043,1085,1086,1084] + </pre> + + <p>See the <seealso marker="stdlib:unicode">unicode</seealso> module + for more details.</p> + + <p>In the following example we will use this ASN.1 specification:</p> + <pre> +UTF DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + UTF ::= UTF8String +END + </pre> + + <p>Encoding and decoding a string with Unicode characters:</p> + <pre> -1> <input>asn1ct:compile('UTF',[ber]).</input> -Erlang ASN.1 version "1.4.3.3" compiling "UTF.asn" -Compiler Options: [ber] ---{generated,"UTF.asn1db"}-- ---{generated,"UTF.erl"}-- +5> <input>asn1ct:compile('UTF', [ber]).</input> ok -2> <input>UTF8Val1 = "hello".</input> -"hello" -3> <input>{ok,UTF8bin1} = asn1rt:utf8_list_to_binary(UTF8Val1).</input> -{ok,<<104,101,108,108,111>>} -4> <input>{ok,B}='UTF':encode('UTF',UTF8bin1).</input> -{ok,[12, - 5, - <<104,101,108,108,111>>]} -5> <input>Bin = list_to_binary(B).</input> -<<12,5,104,101,108,108,111>> -6> <input>{ok,UTF8bin1}='UTF':decode('UTF',Bin).</input> -{ok,<<104,101,108,108,111>>} -7> <input>asn1rt:utf8_binary_to_list(UTF8bin1).</input> -{ok,"hello"} -8> <input>UTF8Val2 = [16#00,16#100,16#ffff,16#ffffff].</input> -[0,256,65535,16777215] -9> <input>{ok,UTF8bin2} = asn1rt:utf8_list_to_binary(UTF8Val2).</input> -{ok,<<0,196,128,239,191,191,248,191,191,191,191>>} -10> <input>{ok,B2} = 'UTF':encode('UTF',UTF8bin2).</input> -{ok,[12, - 11, - <<0,196,128,239,191,191,248,191,191,191,191>>]} -11> <input>Bin2 = list_to_binary(B2).</input> -<<12,11,0,196,128,239,191,191,248,191,191,191,191>> -12> <input>{ok,UTF8bin2} = 'UTF':decode('UTF',Bin2).</input> -{ok,<<0,196,128,239,191,191,248,191,191,191,191>>} -13> <input>asn1rt:utf8_binary_to_list(UTF8bin2).</input> -{ok,[0,256,65535,16777215]} -14> </pre> +6> <input>{ok,Bytes1} = 'UTF':encode('UTF', <<"Гном"/utf8>>).</input> +{ok,<<12,8,208,147,208,189,208,190,208,188>>} +7> <input>{ok,Bin1} = 'UTF':decode('UTF', Bytes1).</input> +{ok,<<208,147,208,189,208,190,208,188>>} +8> <input>io:format("~ts\n", [Bin1]).</input> +Гном +ok +9> <input>unicode:characters_to_list(Bin1).</input> +[1043,1085,1086,1084] + </pre> </section> <section> @@ -887,9 +864,11 @@ OidVal1 = {1,2,55}, <section> <marker id="Object Descriptor"></marker> <title>Object Descriptor</title> - <p>Values of this type can be assigned a value as an ordinary string i.e. <br></br> + <p>Values of this type can be assigned a value as an ordinary string + like this:</p> - "This is the value of an Object descriptor"</p> + <pre> + "This is the value of an Object descriptor"</pre> </section> <section> @@ -932,19 +911,31 @@ Pdu ::= SEQUENCE { <pre> MyPdu = #'Pdu'{a=22,b=77.99,c={0,1,2,3,4},d='NULL'}. </pre> <p>The decode functions will return a record as result when decoding - a <c>SEQUENCE</c> or a <c>SET</c>. - <marker id="DEFAULT"></marker> -</p> - <p>A <c>SEQUENCE</c> and a <c>SET</c> may contain a component with a - <c>DEFAULT</c> key word followed by the actual value that is the - default value. In case of BER encoding it is optional to encode the - value if it equals the default value. If the application uses the - atom asn1_DEFAULT as value or if the value is a primitive value - that equals the default value the encoding omits the bytes for - this value, which is more efficient and it results in fever - bytes to send to the receiving application.</p> - <p>For instance, if the following types exists in a file "File.asn":</p> + a <c>SEQUENCE</c> or a <c>SET</c>.</p> + + <p>A <c>SEQUENCE</c> and a <c>SET</c> may contain a component + with a <c>DEFAULT</c> key word followed by the actual value that + is the default value. The <c>DEFAULT</c> keyword means that the + application doing the encoding can omit encoding of the value, + thus resulting in fewer bytes to send to the receiving + application.</p> + + <p>An application can use the atom <c>asn1_DEFAULT</c> to indicate + that the encoding should be omitted for that position in + the SEQUENCE.</p> + + <p>Depending on the encoding rules, the encoder may also compare + the given value to the default value and automatically omit the + encoding if they are equal. How much effort the encoder makes to + to compare the values depends on the encoding rules. The DER + encoding rules forbids encoding a value equal to the default value, + so it has a more thorough and time-consuming comparison than the + encoders for the other encoding rules.</p> + + <p>In the following example we will use this ASN.1 specification:</p> <pre> +File DEFINITIONS AUTOMATIC TAGS ::= +BEGIN Seq1 ::= SEQUENCE { a INTEGER DEFAULT 1, b Seq2 DEFAULT {aa TRUE, bb 15} @@ -954,131 +945,50 @@ Seq2 ::= SEQUENCE { aa BOOLEAN, bb INTEGER } - </pre> - <p>Some values and the corresponding encoding in an Erlang terminal - is shown below:</p> + +Seq3 ::= SEQUENCE { + bs BIT STRING {a(0), b(1), c(2)} DEFAULT {a, c} +} +END </pre> + <p>Here is an example where the BER encoder is able to omit encoding + of the default values:</p> <pre> -1> <input>asn1ct:compile('File').</input> -Erlang ASN.1 version "1.3.2" compiling "File.asn1" -Compiler Options: [] ---{generated,"File.asn1db"}-- ---{generated,"File.hrl"}-- ---{generated,"File.erl"}-- +1> <input>asn1ct:compile('File', [ber]).</input> ok -2> <input>'File':encode('Seq1',{'Seq1',asn1_DEFAULT,asn1_DEFAULT}).</input> -{ok,["0",[0],[[],[]]]} -3> <input>lists:flatten(["0",[0],[[],[]]]).</input> -[48,0] -4> <input>'File':encode('Seq1',{'Seq1',1,{'Seq2',true,15}}).</input> -{ok,["0","\\b",[[],["\\241",[6],[[[128],[1],"\\377"],[[129],[1],[15]]]]]]} -5> <input>lists:flatten(["0","\\b",[[],["\\241",[6],[[[128],[1],"\\377"],[[129],[1],[15]]]]]]).</input> -[48,8,161,6,128,1,255,129,1,15] -6> </pre> - <p>The result after command line 3, in the example above,shows that the - encoder omits the encoding of default values when they are specific - by asn1_DEFAULT. Line 5 shows that even primitive values that equals - the default value are detected and not encoded. But the constructed - value of component <c>b</c> in <c>Seq1</c> is not recognized as the - default value. Checking of default values in <c>BER</c> is not done - in case of complex values, because it would be to expensive. - <marker id="DEFAULT DER"></marker> -</p> - <p>But, the DER encoding format has stronger requirements regarding - default values both for SET and SEQUENCE. A more elaborate and time - expensive check of default values will take place. The following is - an example with the same types and values as above but with der - encoding format.</p> - <pre> -1> <input>asn1ct:compile('File',[der]).</input> -Erlang ASN.1 version "1.3.2" compiling "File.asn1" -Compiler Options: [der] ---{generated,"File.asn1db"}-- ---{generated,"File.hrl"}-- ---{generated,"File.erl"}-- +2> <input>'File':encode('Seq1', {'Seq1',asn1_DEFAULT,asn1_DEFAULT}).</input> +{ok,<<48,0>>} +3> <input>'File':encode('Seq1', {'Seq1',1,{'Seq2',true,15}}).</input> +{ok,<<48,0>>} </pre> + + <p>And here is an example with a named BIT STRING where the BER + encoder will not omit the encoding:</p> + <pre> +4> <input>'File':encode('Seq3', {'Seq3',asn1_DEFAULT).</input> +{ok,<<48,0>>} +5> <input>'File':encode('Seq3', {'Seq3',<<16#101:3>>).</input> +{ok,<<48,4,128,2,5,160>>} </pre> + + <p>The DER encoder will omit the encoding for the same BIT STRING:</p> + <pre> +6> <input>asn1ct:compile('File', [ber,der]).</input> ok -2> <input>'File':encode('Seq1',{'Seq1',asn1_DEFAULT,asn1_DEFAULT}).</input> -{ok,["0",[0],[[],[]]]} -3> <input>lists:flatten(["0",[0],[[],[]]]).</input> -[48,0] -4> <input>'File':encode('Seq1',{'Seq1',1,{'Seq2',true,15}}).</input> -{ok,["0",[0],[[],[]]]} -5> <input>lists:flatten(["0",[0],[[],[]]]).</input> -[48,0] -6> - </pre> - <p>Line 5 shows that even values of constructed types is checked and if - it equals the default value it will not be encoded.</p> +7> <input>'File':encode('Seq3', {'Seq3',asn1_DEFAULT).</input> +{ok,<<48,0>>} +8> <input>'File':encode('Seq3', {'Seq3',<<16#101:3>>).</input> +{ok,<<48,0>>} </pre> </section> <section> <marker id="SET"></marker> <title>SET</title> - <p>The SET type is an unusual construct and normally the SEQUENCE - type is more appropriate to use. Set is also inefficient compared with SEQUENCE, as the components can be in any order. Hence, it must be possible - to distinguish every component in 'SET', both when - encoding and decoding a value of a type defined to be a SET. - The tags of all components must be different from each other - in order to be easily recognizable.</p> - <p>A SET may be defined as:</p> - <pre> -Pdu2 ::= SET { - a INTEGER, - b BOOLEAN, - c ENUMERATED {on(0),off(1)} } </pre> - <p>A SET is represented as an Erlang record. - For each SEQUENCE and <c>SET</c> in - an ASN.1 module an Erlang record declaration is generated. For - <c>Pdu2</c> above a record is defined like this:</p> - <pre> --record('Pdu2',{a, b, c}). </pre> - <p>The record declarations for a module <c>M</c> are placed in a - separate <c>M.hrl</c> file.</p> - <p>Values can be assigned in Erlang as demonstrated below:</p> - <pre> -V = #'Pdu2'{a=44,b=false,c=off}. </pre> - <p>The decode functions will return a record as result when decoding - a SET. - </p> - <p>The difference between SET and SEQUENCE is that the order of - the components (in the BER encoded format) is undefined for SET - and defined as the lexical order from the ASN.1 definition for - SEQUENCE. The ASN.1 compiler for Erlang will always encode a - SET in the lexical order. The decode routines can handle SET - components encoded in any order but will always return the - result as a record. Since all components of the SET must be - distinguishable both in the encoding phase as well as the - decoding phase the following type is not allowed in a module - with EXPLICIT or IMPLICIT as tag-default :</p> - <p></p> - <pre> -Bad ::= SET {i INTEGER, - j INTEGER } </pre> - <p>The ASN.1 to Erlang compiler rejects the above type. We - shall not explain the concept of tag further here, we refer to - [<cite id="X.680"></cite>]. - </p> - <p>Encoding of a SET with components with DEFAULT values behaves - similar as a SEQUENCE, <seealso marker="#DEFAULT">see above</seealso>. The DER encoding format restrictions on DEFAULT - values is the same for SET as for SEQUENCE, and is supported by - the compiler, <seealso marker="#DEFAULT DER">see above</seealso>.</p> - <p>Moreover, in DER the elements of a SET will be sorted. If a - component is an un-tagged choice the sorting have to take place - in run-time. This fact emphasizes the following recommendation - if DER encoding format is used.</p> - <p>The concept of SET is an unusual - construct and one cannot think of one single application - where the set type is essential. (Imagine if someone - "invented'' the shuffled array in 'C') People tend to think - that 'SET' sounds nicer and more mathematical than 'SEQUENCE' - and hence use it when 'SEQUENCE' would have been more - appropriate. It is also most inefficient, since every correct - implementation of SET must always be prepared to accept the - components in any order. So, if possible use SEQUENCE instead - of SET.</p> + <p>In Erlang, the SET type is used exactly as SEQUENCE. Note + that if the BER or DER encoding rules are used, decoding a + SET is slower than decoding a SEQUENCE because the components + must be sorted.</p> </section> <section> - <title>Notes about Extend-ability for SEQUENCE and SET</title> + <title>Notes about extensibility for SEQUENCE and SET</title> <p>When a SEQUENCE or SET contains an extension marker and extension components like this:</p> <pre> @@ -1105,51 +1015,28 @@ SExt ::= SEQUENCE { <marker id="CHOICE"></marker> <title>CHOICE</title> <p>The CHOICE type is a space saver and is similar to the concept of a - 'union' in the C-language. As with the previous SET-type, the - tags of all components of a CHOICE need to be distinct. If - AUTOMATIC TAGS are defined for the module (which is - preferable) the tags can be omitted completely in the ASN.1 - specification of a CHOICE. - </p> + 'union' in the C language.</p> <p>Assume:</p> <pre> +SomeModuleName DEFINITIONS AUTOMATIC TAGS ::= +BEGIN T ::= CHOICE { - x [0] REAL, - y [1] INTEGER, - z [2] OBJECT IDENTIFIER } - </pre> + x REAL, + y INTEGER, + z OBJECT IDENTIFIER } +END </pre> <p>It is then possible to assign values:</p> <pre> TVal1 = {y,17}, TVal2 = {z,{0,1,2}}, </pre> - <p>A CHOICE value is always represented as the tuple + <p>A CHOICE value is always represented as the tuple <c>{ChoiceAlternative, Val}</c> where <c>ChoiceAlternative</c> - is an atom denoting the selected choice - alternative. - </p> - <p>It is also allowed to have a CHOICE type tagged as follow:</p> - <p></p> - <pre> -C ::= [PRIVATE 111] CHOICE { - C1, - C2 } - -C1 ::= CHOICE { - a [0] INTEGER, - b [1] BOOLEAN } - -C2 ::= CHOICE { - c [2] INTEGER, - d [3] OCTET STRING } </pre> - <p>In this case, the top type C appears to have no tags at all in - its components, however, both C1 and C2 are also defined as - CHOICE types and they have distinct tags among themselves. - Hence, the above type C is both legal and allowed. + is an atom denoting the selected choice alternative. </p> <section> - <title>Extendable CHOICE</title> + <title>Extensible CHOICE</title> <p>When a CHOICE contains an extension marker and the decoder detects an unknown alternative of the CHOICE the value is represented as:</p> <pre> @@ -1226,26 +1113,29 @@ Arr2Val = ["abc",[14,34,54],"Octets"], </pre> Where <c>Value</c> may be a value of yet another type T2.</p> <p>For example:</p> <pre> +EmbeddedExample DEFINITIONS AUTOMATIC TAGS ::= +BEGIN B ::= SEQUENCE { a Arr1, - b [0] T } + b T } Arr1 ::= SET SIZE (5) OF INTEGER (4..9) T ::= CHOICE { - x [0] REAL, - y [1] INTEGER, - z [2] OBJECT IDENTIFIER } </pre> - <p>The above example can be assigned like this in Erlang:</p> + x REAL, + y INTEGER, + z OBJECT IDENTIFIER } + END </pre> + <p>The SEQUENCE b can be encoded like this in Erlang:</p> <pre> -V2 = #'B'{a=[4,5,6,7,8], b={x,7.77}}. - </pre> +1> 'EmbeddedExample':encode('B', {'B',[4,5,6,7,8],{x,"7.77"}}). +{ok,<<5,56,0,8,3,55,55,55,46,69,45,50>>} </pre> </section> </section> <section> <title>Naming of Records in .hrl Files</title> - <p>When an asn1 specification is compiled all defined types of + <p>When an ASN.1 specification is compiled all defined types of type SET or SEQUENCE will result in a corresponding record in the generated hrl file. This is because the values for SET/SEQUENCE as mentioned in sections above are represented as records.</p> @@ -1261,8 +1151,8 @@ V2 = #'B'{a=[4,5,6,7,8], b={x,7.77}}. Emb ::= SEQUENCE { a SEQUENCE OF OCTET STRING, b SET { - a [0] INTEGER, - b [1] INTEGER DEFAULT 66}, + a INTEGER, + b INTEGER DEFAULT 66}, c CHOICE { a INTEGER, b FooType } } @@ -1333,7 +1223,7 @@ PType{T} ::= SEQUENCE{ <p>Types may refer to themselves. Suppose:</p> <pre> Rec ::= CHOICE { - nothing [0] NULL, + nothing NULL, something SEQUENCE { a INTEGER, b OCTET STRING, @@ -1365,7 +1255,7 @@ tt TT ::= {a 77,b {"kalle","kula"}} </pre> Firstly, it could be used as the value in some DEFAULT component:</p> <pre> SS ::= SET { - s [0] OBJECT IDENTIFIER, + s OBJECT IDENTIFIER, val TT DEFAULT tt } </pre> <p>It could also be used from inside an Erlang program. If the above ASN.1 code was defined in ASN.1 module <c>Values</c>, then the ASN.1 value @@ -1399,8 +1289,8 @@ SS ::= SET { <marker id="Information Object"></marker> <title>ASN.1 Information Objects (X.681)</title> <p>Information Object Classes, Information Objects and Information - Object Sets, (in the following called classes, objects and - object sets respectively), are defined in the standard + Object Sets (in the following called classes, objects and + object sets respectively) are defined in the standard definition [<cite id="X.681"></cite>]. In the following only a brief explanation is given. </p> <p>These constructs makes it possible to define open types, @@ -1469,9 +1359,26 @@ StartMessage ::= SEQUENCE { <p><c>StartMessage</c> can in the <c>content</c> field be encoded with a value of any type that an object in the <c>GENERAL-PROCEDURES</c> object set has in its <c>NEW MESSAGE</c> field. This field refers to a type field - <c><![CDATA[&Message]]></c> in the class. The <c>msgId</c> field is always + <c>&Message</c> in the class. The <c>msgId</c> field is always encoded as a PrintableString, since the field refers to a fixed type in the class.</p> + <p>In practice, object sets are usually declared to be extensible so + so that more objects can be added to the set later. Extensibility is + indicated like this:</p> + <pre> +GENERAL-PROCEDURES GENERAL-PROCEDURE ::= { + object1 | object2, ...} </pre> + <p>When decoding a type that uses an extensible set constraint, + there is always the possibility that the value in the UNIQUE + field is unknown (i.e. the type has been encoded with a later + version of the ASN.1 specification). When that happens, the + unencoded data will be returned wrapped in a tuple like this:</p> + + <pre> +{asn1_OPENTYPE,Binary}</pre> + <p>where <c>Binary</c> is an Erlang binary that contains the encoded + data. (If the option <c>legacy_erlang_types</c> has been given, + just the binary will be returned.)</p> </section> <section> @@ -1483,7 +1390,7 @@ StartMessage ::= SEQUENCE { instance, if a Type is used in a definition with certain purpose, one want the type-name to express the intention. This can be done with parameterization.</p> - <p>When many types (or an other ASN.1 entity) only differs in some + <p>When many types (or another ASN.1 entity) only differs in some minor cases, but the structure of the types are similar, only one general type can be defined and the differences may be supplied through parameters. </p> @@ -1500,132 +1407,11 @@ T1 ::= General{PrintableString} T2 ::= General{BIT STRING} </pre> <p>An example of a value that can be encoded as type T1 is {12,"hello"}.</p> - <p>Observe that the compiler not generates encode/decode functions for - parameterized types, only for the instances of the parameterized - types. So, if a file contains the types General{}, T1 and T2 above, + <p>Note that the compiler does not generate encode/decode functions for + parameterized types, but only for the instances of the parameterized + types. Therefore, if a file contains the types General{}, T1 and T2 above, encode/decode functions will only be generated for T1 and T2. </p> </section> - - <section> - <title>Tags</title> - <p>Every built-in ASN.1 type, except CHOICE and ANY have a universal tag. - This is a unique number that clearly identifies the type. <br></br> - - It is essential for all users of ASN.1 to - understand all the details about tags.</p> - <p>Tags are implicitly encoded in the BER encoding as shown below, but - are hardly not accounted for in the PER encoding. In PER tags are - used for instance to sort the components of a SET.</p> - <p>There are four different types of tags.</p> - <taglist> - <tag><em>universal</em></tag> - <item> - <p>For types whose meaning is the same in all - applications. Such as integers, sequences and so on; that is, all the built in - types.</p> - </item> - <tag><em>application</em></tag> - <item> - <p>For application specific types for example, the types in - X.400 Message handling service have this sort of tag.</p> - </item> - <tag><em>private</em></tag> - <item> - <p>For your own private types.</p> - </item> - <tag><em>context</em></tag> - <item> - <p>This is used to distinguish otherwise indistinguishable - types in a specific context. For example, if we have two - components of a - CHOICE type that are both <c>INTEGER</c> values, there is no - way for the decoder to - decipher which component was actually chosen, since both - components will be - tagged as <c>INTEGER</c>. When this or similar situations occur, - one or both of the components should be given a context specific - to resolve the ambiguity.</p> - </item> - </taglist> - <p>The tag in the case of the 'Apdu' type [PRIVATE 1] is encoded to a - sequence of bytes making it possible for a - decoder to look at the (initial) bytes that arrive and determine - whether the rest of the bytes must be of the type associated - with that particular sequence of bytes. This means that each - tag must be uniquely associated with <em>only</em> one ASN.1 - type. - </p> - <p>Immediately following the tag is a sequence of bytes - informing the decoder of the length of the instance. This is - sometimes referred to as TLV (Tag length value) encoding. - Hence, the structure of a BER encoded series of bytes is as shown in the table below.</p> - <p></p> - <table> - <row> - <cell align="left" valign="middle">Tag</cell> - <cell align="left" valign="middle">Len</cell> - <cell align="left" valign="middle">Value</cell> - </row> - <tcaption>Structure of a BER encoded series of bytes</tcaption> - </table> - </section> - - <section> - <title>Encoding Rules</title> - <p>When the first recommendation on ASN.1 was released 1988 it was - accompanied with the Basic Encoding Rules, BER, as the only - alternative for encoding. - BER is a somewhat verbose protocol. It adopts a so-called TLV (type, - length, value) approach to encoding in which every element of the - encoding carries some type information, some length information and - then the value of that element. Where the element is itself - structured, then the Value part of the element is itself a series of - embedded TLV components, to whatever depth is necessary. In summary, - BER is not a compact encoding but is relatively fast and easy to - produce.</p> - <p>The DER (Distinguished Encoding Rule) encoding format was included in - the standard in 1994. It is a specialized form of BER, which gives - the encoder the option to encode some entities differently. For - instance, is the value for TRUE any octet with any bit set to one. But, - DER does not leave any such choices. The value for TRUE in the DER - case is encoded as the octet <c>11111111</c>. So, the same value - encoded by two different DER encoders must result in the same bit - stream.</p> - <p>A more compact encoding is achieved with the Packed Encoding - Rules PER which was introduced together with the revised - recommendation in 1994. PER takes a rather different approach from - that taken by BER. The first difference is that the tag part in - the TLV is omitted from the encodings, and any tags in the - notation are not encoded. The potential ambiguities are resolved - as follows:</p> - <list type="bulleted"> - <item> - <p>A CHOICE is encoded by first encoding a choice index which - identifies the chosen - alternative by its position in the notation.</p> - </item> - <item> - <p>The elements of a SEQUENCE are transmitted in textual - order. OPTIONAL or DEFAULT elements are preceded by a bit map - to identify which elements are present. After sorting the - elements of a SET in the "canonical tag order" as defined in - X.680 8.6 they are treated as a SEQUENCE regarding OPTIONAL - and DEFAULT elements. A SET is transferred in the sorted - order.</p> - </item> - </list> - <p>A second difference is that PER takes full account of the sub-typing - information in that the encoded bytes are affected by the constraints. - The BER encoded bytes are unaffected by the constraints. - PER uses the sub-typing information to for example omit length fields - whenever possible. </p> - <p>The run-time functions, sometimes take the constraints into account - both for BER and PER. For instance are SIZE constrained strings checked.</p> - <p>There are two variants of PER, <em>aligned</em> and <em>unaligned</em>. - In summary, PER results in compact encodings which require much more - computation to produce than BER. - </p> - </section> </chapter> diff --git a/lib/asn1/doc/src/asn1ct.xml b/lib/asn1/doc/src/asn1ct.xml index fcd77e9dc6..32ff2d52cf 100644 --- a/lib/asn1/doc/src/asn1ct.xml +++ b/lib/asn1/doc/src/asn1ct.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> @@ -42,6 +42,18 @@ can be used in during development of applications which handles ASN.1 data (encoded as BER or PER).</p> <note> + <p>By default in OTP 17, the representation of the BIT STRING + and OCTET STRING types as Erlang terms have changed. BIT + STRING values are now Erlang bitstrings and OCTET STRING values + are binaries. Also, an undecoded open type will now be wrapped in + a <c>asn1_OPENTYPE</c> tuple. For details see <seealso + marker="asn1_ug#BIT STRING">BIT STRING</seealso>, <seealso + marker="asn1_ug#OCTET STRING">OCTET STRING</seealso>, and + <seealso marker="asn1_ug#Information%20Object">ASN.1 Information Objects</seealso> in User's Guide.</p> + <p>To revert to the old representation of the types, use the + <c>legacy_erlang_types</c> option.</p> + </note> + <note> <p>In R16, the options have been simplified. The back-end is chosen using one of the options <c>ber</c>, <c>per</c>, or <c>uper</c>. The options <c>optimize</c>, <c>nif</c>, and <c>driver</c> options @@ -50,7 +62,7 @@ and <c>uper_bin</c> options will still work, but will print a warning. </p> <p>Another change in R16 is that the generated <c>encode/2</c> - function (and <c>asn1rt:encode/3</c>) always returns a binary. + function always returns a binary. The <c>encode/2</c> function for the BER back-end used to return an iolist.</p> </note> @@ -64,7 +76,7 @@ <v>Asn1module = atom() | string()</v> <v>Options = [Option| OldOption]</v> <v>Option = ber | per | uper | der | compact_bit_string | - legacy_bit_string | + legacy_bit_string | legacy_erlang_types | noobj | {n2n, EnumTypeName} |{outdir, Dir} | {i, IncludeDir} | asn1config | undec_rest | no_ok_wrapper | {macro_name_prefix, Prefix} | {record_name_prefix, Prefix} | verbose | warnings_as_errors</v> @@ -163,6 +175,7 @@ File3.asn </pre> BIT STRING type section in the Users Guide </seealso>. </p> + <p>This option implies the <c>legacy_erlang_types</c> option.</p> </item> <tag><c>legacy_bit_string</c></tag> <item> @@ -175,8 +188,19 @@ File3.asn </pre> <seealso marker="asn1_ug#BIT STRING"> BIT STRING type section in the Users Guide </seealso>. + <p>This option implies the <c>legacy_erlang_types</c> option.</p> </p> </item> + <tag><c>legacy_erlang_types</c></tag> + <item> + <p>Use the same Erlang types to represent BIT STRING and + OCTET STRING as in R16. For details see <seealso + marker="asn1_ug#BIT STRING">BIT STRING</seealso> and + <seealso marker="asn1_ug#OCTET STRING">OCTET + STRING</seealso> in User's Guide.</p> + <p><em>This option is not recommended for + new code.</em></p> + </item> <tag><c>{n2n, EnumTypeName}</c></tag> <item> <p> @@ -303,6 +327,8 @@ File3.asn </pre> not always checked. Returns <c>{ok, Bytes}</c> if successful or <c>{error, Reason}</c> if an error occurred. </p> + <p>This function is deprecated. + Use <c>Module:encode(Type, Value)</c> instead.</p> </desc> </func> <func> @@ -316,6 +342,8 @@ File3.asn </pre> <desc> <p>Decodes <c>Type</c> from <c>Module</c> from the binary <c>Bytes</c>. Returns <c>{ok, Value}</c> if successful.</p> + <p>This function is deprecated. + Use <c>Module:decode(Type, Bytes)</c> instead.</p> </desc> </func> <func> diff --git a/lib/asn1/doc/src/asn1rt.xml b/lib/asn1/doc/src/asn1rt.xml index 2b1b108ab1..3cf56b01ca 100644 --- a/lib/asn1/doc/src/asn1rt.xml +++ b/lib/asn1/doc/src/asn1rt.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> @@ -34,9 +34,12 @@ <module>asn1rt</module> <modulesummary>ASN.1 runtime support functions</modulesummary> <description> - <p>This module is the interface module for the ASN.1 runtime support functions. - To encode and decode ASN.1 types in runtime the functions in this module - should be used.</p> + <warning> + <p> + All functions in this module are deprecated and will be + removed in a future release. + </p> + </warning> </description> <funcs> @@ -52,6 +55,7 @@ <desc> <p>Decodes <c>Type</c> from <c>Module</c> from the binary <c>Bytes</c>. Returns <c>{ok,Value}</c> if successful.</p> + <p>Use <c>Module:decode(Type, Bytes)</c> instead of this function.</p> </desc> </func> @@ -65,16 +69,13 @@ <v>Reason = term()</v> </type> <desc> - <p>Encodes <c>Value</c> of <c>Type</c> defined in the ASN.1 module - <c>Module</c>. Returns a possibly nested list of bytes and or binaries - if successful. To get as fast execution as possible the - encode function only performs rudimentary tests that the input - <c>Value</c> - is a correct instance of <c>Type</c>. The length of strings is for example - not always checked. </p> - <note> - <p>Starting in R16, <c>Bytes</c> is always a binary.</p> - </note> + <p>Encodes <c>Value</c> of <c>Type</c> defined in the ASN.1 + module <c>Module</c>. Returns a binary if successful. To get + as fast execution as possible the encode function only + performs rudimentary tests that the input <c>Value</c> is a + correct instance of <c>Type</c>. The length of strings is, for + example, not always checked. </p> + <p>Use <c>Module:encode(Type, Value)</c> instead of this function.</p> </desc> </func> @@ -90,6 +91,7 @@ <p><c>info/1</c> returns the version of the asn1 compiler that was used to compile the module. It also returns the compiler options that was used.</p> + <p>Use <c>Module:info()</c> instead of this function.</p> </desc> </func> @@ -106,6 +108,7 @@ to a list of integers, where each integer represents one character as its unicode value. The function fails if the binary is not a properly encoded UTF8 string.</p> + <p>Use <seealso marker="stdlib:unicode#characters_to_list-1">unicode:characters_to_list/1</seealso> instead of this function.</p> </desc> </func> @@ -121,6 +124,7 @@ <p><c>utf8_list_to_binary/1</c> Transforms a list of integers, where each integer represents one character as its unicode value, to a UTF8 encoded binary.</p> + <p>Use <seealso marker="stdlib:unicode#characters_to_binary-1">unicode:characters_to_binary/1</seealso> instead of this function.</p> </desc> </func> diff --git a/lib/asn1/doc/src/book.xml b/lib/asn1/doc/src/book.xml index 718e6e7b17..2399267cb0 100644 --- a/lib/asn1/doc/src/book.xml +++ b/lib/asn1/doc/src/book.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="iso-8859-1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE book SYSTEM "book.dtd"> <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/asn1/doc/src/fascicules.xml b/lib/asn1/doc/src/fascicules.xml index 2488e7b57e..837b4f57f4 100644 --- a/lib/asn1/doc/src/fascicules.xml +++ b/lib/asn1/doc/src/fascicules.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="iso-8859-1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE fascicules SYSTEM "fascicules.dtd"> <fascicules> diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml index a00fc3de36..a7032737bd 100644 --- a/lib/asn1/doc/src/notes.xml +++ b/lib/asn1/doc/src/notes.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -31,6 +31,153 @@ <p>This document describes the changes made to the asn1 application.</p> +<section><title>Asn1 3.0.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Several problems where the ASN.1 compiler would crash + when attempting to compile correct specifications have + been corrected.</p> + <p> + Own Id: OTP-12125</p> + </item> + <item> + <p> + Robustness when decoding incorrect BER messages has been + improved.</p> + <p> + Own Id: OTP-12145</p> + </item> + </list> + </section> + +</section> + +<section><title>Asn1 3.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The ASN.1 compiler now generates code that don't trigger + Dialyzer warnings. Along the way, a few minor bugs were + fixed.</p> + <p> + Own Id: OTP-11372 Aux Id: seq12397 </p> + </item> + </list> + </section> + +</section> + +<section><title>Asn1 3.0</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Subtyping an extensible ENUMERATED would cause an + compilation error. (Thanks to Morten Nygaard Åsnes for + reporting this bug.)</p> + <p> + Own Id: OTP-11700</p> + </item> + <item> + <p>When specifying the value for an OCTET STRING in a + specification, the ASN.1 standard clearly states that the + value must be either a bstring or an hstring, but NOT a + cstring. The ASN.1 compiler will now generate a + compilation error if the value of an OCTET STRING is + given as a character string.</p> + <p>That is, the following example is now illegal:</p> + <p><c>string OCTET STRING ::= "Now illegal"</c></p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-11727</p> + </item> + <item> + <p> + Application upgrade (appup) files are corrected for the + following applications: </p> + <p> + <c>asn1, common_test, compiler, crypto, debugger, + dialyzer, edoc, eldap, erl_docgen, et, eunit, gs, hipe, + inets, observer, odbc, os_mon, otp_mibs, parsetools, + percept, public_key, reltool, runtime_tools, ssh, + syntax_tools, test_server, tools, typer, webtool, wx, + xmerl</c></p> + <p> + A new test utility for testing appup files is added to + test_server. This is now used by most applications in + OTP.</p> + <p> + (Thanks to Tobias Schlager)</p> + <p> + Own Id: OTP-11744</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + By giving --enable-static-{nifs,drivers} to configure it + is now possible to statically linking of nifs and drivers + to the main Erlang VM binary. At the moment only the asn1 + and crypto nifs of the Erlang/OTP nifs and drivers have + been prepared to be statically linked. For more details + see the Installation Guide in the System documentation.</p> + <p> + Own Id: OTP-11258</p> + </item> + <item> + <p>Code generation for the <c>per</c> and <c>uper</c> + backends has been somewhat improved.</p> + <p> + Own Id: OTP-11573</p> + </item> + <item> + <p>The OCTET STRING and BIT STRING types now have a more + natural mapping to Erlang types (binary and bitstring, + respectively), which is more efficient and will avoid + useless conversions between lists and + binaries/bitstrings.</p> + <p>This is an incompatible change. To revert to the old + mapping to support existing applications, use the + <c>legacy_erlang_types</c> option.</p> + <p>Impact: There is a potential for better performance, + as it is now possible to avoid conversions between lists + and binaries both in the generated ASN.1 encode/decode + code and in the application itself.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-11594</p> + </item> + <item> + <p>All functions in the <c>asn1rt</c> module, as well as + <c>asn1ct:decode/3</c> and <c>asn1ct:encode/3</c>, are + now deprecated.</p> + <p> + Own Id: OTP-11731</p> + </item> + <item> + <p> + Generated .hrl files are now protected from being + included more than once.</p> + <p> + Own Id: OTP-11804</p> + </item> + </list> + </section> + +</section> + <section><title>Asn1 2.0.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/asn1/doc/src/part.xml b/lib/asn1/doc/src/part.xml index 19ee64b4a0..735ec2e616 100644 --- a/lib/asn1/doc/src/part.xml +++ b/lib/asn1/doc/src/part.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/asn1/doc/src/ref_man.xml b/lib/asn1/doc/src/ref_man.xml index a0af1f5be3..0a0ed5416a 100644 --- a/lib/asn1/doc/src/ref_man.xml +++ b/lib/asn1/doc/src/ref_man.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="iso-8859-1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE application SYSTEM "application.dtd"> <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/asn1/src/Makefile b/lib/asn1/src/Makefile index 500f4a1358..6798da0072 100644 --- a/lib/asn1/src/Makefile +++ b/lib/asn1/src/Makefile @@ -52,6 +52,7 @@ CT_MODULES= \ asn1ct_pretty_format \ asn1ct_func \ asn1ct_gen \ + asn1ct_gen_check \ asn1ct_gen_per \ asn1ct_name \ asn1ct_constructed_per \ diff --git a/lib/asn1/src/asn1.app.src b/lib/asn1/src/asn1.app.src index f2ee8deb75..02cbba0f10 100644 --- a/lib/asn1/src/asn1.app.src +++ b/lib/asn1/src/asn1.app.src @@ -10,5 +10,6 @@ asn1db ]}, {env, []}, - {applications, [kernel, stdlib]} + {applications, [kernel, stdlib]}, + {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-6.0"]} ]}. diff --git a/lib/asn1/src/asn1.appup.src b/lib/asn1/src/asn1.appup.src index 2d11eddfbf..e4b3508cc4 100644 --- a/lib/asn1/src/asn1.appup.src +++ b/lib/asn1/src/asn1.appup.src @@ -1,11 +1,21 @@ +%% -*- erlang -*- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% {"%VSN%", -% This version does not change anything of the runtime modules -% Only changes in compile time modules and thus no need for upgrade on target -[ - ], - [ - ]}. - - - - + [{<<".*">>,[{restart_application, asn1}]}], + [{<<".*">>,[{restart_application, asn1}]}] +}. diff --git a/lib/asn1/src/asn1_records.hrl b/lib/asn1/src/asn1_records.hrl index 396ba0fcfa..6c1cf1b12a 100644 --- a/lib/asn1/src/asn1_records.hrl +++ b/lib/asn1/src/asn1_records.hrl @@ -37,7 +37,7 @@ -record('ObjectClassFieldType',{classname,class,fieldname,type}). -record(typedef,{checked=false,pos,name,typespec}). --record(classdef,{checked=false,pos,name,typespec}). +-record(classdef, {checked=false,pos,name,module,typespec}). -record(valuedef,{checked=false,pos,name,type,value,module}). -record(ptypedef,{checked=false,pos,name,args,typespec}). -record(pvaluedef,{checked=false,pos,name,args,type,value}). @@ -45,7 +45,6 @@ -record(pobjectdef,{checked=false,pos,name,args,class,def}). -record(pobjectsetdef,{checked=false,pos,name,args,class,def}). --record(identifier,{pos,val}). -record('Constraint',{'SingleValue'=no,'SizeConstraint'=no,'ValueRange'=no,'PermittedAlphabet'=no, 'ContainedSubtype'=no, 'TypeConstraint'=no,'InnerSubtyping'=no,e=no,'Other'=no}). -record(simpletableattributes,{objectsetname,c_name,c_index,usedclassfield, @@ -73,6 +72,15 @@ % Externalvaluereference -> modulename '.' typename -record('Externalvaluereference',{pos,module,value}). +%% Used to hold a tag for a field in a SEQUENCE/SET. It can also +%% be used for identifiers in OBJECT IDENTIFIER values, since the +%% parser cannot always distinguish a SEQUENCE with one element from +%% an OBJECT IDENTIFIER. +-record(seqtag, + {pos :: integer(), + module :: atom(), + val :: atom()}). + -record(state,{module,mname,type,tname,value,vname,erule,parameters=[], inputmodules,abscomppath=[],recordtopname=[],options, sourcedir}). diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index f2ccf5f212..df341e5aab 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -19,6 +19,10 @@ %% %% -module(asn1ct). +-deprecated([decode/3,encode/3]). +-compile([{nowarn_deprecated_function,{asn1rt,decode,3}}, + {nowarn_deprecated_function,{asn1rt,encode,2}}, + {nowarn_deprecated_function,{asn1rt,encode,3}}]). %% Compile Time functions for ASN.1 (e.g ASN.1 compiler). @@ -39,8 +43,8 @@ add_tobe_refed_func/1,add_generated_refed_func/1, maybe_rename_function/3,current_sindex/0, set_current_sindex/1,maybe_saved_sindex/2, - parse_and_save/2,verbose/3,warning/3,warning/4,error/3]). --export([get_bit_string_format/0]). + parse_and_save/2,verbose/3,warning/3,warning/4,error/3,format_error/1]). +-export([get_bit_string_format/0,use_legacy_types/0]). -include("asn1_records.hrl"). -include_lib("stdlib/include/erl_compile.hrl"). @@ -139,7 +143,8 @@ parse_and_save_passes() -> {pass,save,fun save_pass/1}]. common_passes() -> - [{pass,check,fun check_pass/1}, + [{iff,parse,{pass,parse_listing,fun parse_listing/1}}, + {pass,check,fun check_pass/1}, {iff,abs,{pass,abs_listing,fun abs_listing/1}}, {pass,generate,fun generate_pass/1}, {unless,noobj,{pass,compile,fun compile_pass/1}}]. @@ -239,6 +244,16 @@ save_pass(#st{code=M,erule=Erule,dbfile=DbFile}=St) -> asn1_db:dbsave(DbFile,M#module.name), {ok,St}. +parse_listing(#st{code=Code,outfile=OutFile0}=St) -> + OutFile = OutFile0 ++ ".parse", + case file:write_file(OutFile, io_lib:format("~p\n", [Code])) of + ok -> + done; + {error,Reason} -> + Error = {write_error,OutFile,Reason}, + {error,St#st{error=[{structured_error,{OutFile0,none},?MODULE,Error}]}} + end. + abs_listing(#st{code={M,_},outfile=OutFile}) -> pretty2(M#module.name, OutFile++".abs"), done. @@ -333,8 +348,7 @@ print_structured_errors([_|_]=Errors) -> print_structured_errors(_) -> ok. compile1(File, #st{opts=Opts}=St0) -> - verbose("Erlang ASN.1 version ~p, compiling ~p~n", [?vsn,File], Opts), - verbose("Compiler Options: ~p~n", [Opts], Opts), + compiler_verbose(File, Opts), Passes = single_passes(), Base = filename:rootname(filename:basename(File)), OutFile = outfile(Base, "", Opts), @@ -349,8 +363,7 @@ compile1(File, #st{opts=Opts}=St0) -> %% compile_set/3 merges and compiles a number of asn1 modules %% specified in a .set.asn file to one .erl file. compile_set(SetBase, Files, #st{opts=Opts}=St0) -> - verbose("Erlang ASN.1 version ~p compiling ~p ~n", [?vsn,Files], Opts), - verbose("Compiler Options: ~p~n",[Opts], Opts), + compiler_verbose(Files, Opts), OutFile = outfile(SetBase, "", Opts), DbFile = outfile(SetBase, "asn1db", Opts), InputModules = [begin @@ -363,6 +376,11 @@ compile_set(SetBase, Files, #st{opts=Opts}=St0) -> Passes = set_passes(), run_passes(Passes, St). +compiler_verbose(What, Opts) -> + verbose("Erlang ASN.1 compiler ~s\n", [?vsn], Opts), + verbose("Compiling: ~p\n", [What], Opts), + verbose("Options: ~p\n", [Opts], Opts). + %% merge_modules/2 -> returns a module record where the typeorval lists are merged, %% the exports lists are merged, the imports lists are merged when the %% elements come from other modules than the merge set, the tagdefault @@ -559,6 +577,8 @@ get_pos_of_def(#pobjectdef{pos=Pos}) -> Pos; get_pos_of_def(#pobjectsetdef{pos=Pos}) -> Pos; +get_pos_of_def(#'Externaltypereference'{pos=Pos}) -> + Pos; get_pos_of_def(#'Externalvaluereference'{pos=Pos}) -> Pos; get_pos_of_def(_) -> @@ -838,6 +858,7 @@ delete_double_of_symbol1([],Acc) -> generate({M,GenTOrV}, OutFile, EncodingRule, Options) -> debug_on(Options), setup_bit_string_format(Options), + setup_legacy_erlang_types(Options), put(encoding_options,Options), asn1ct_table:new(check_functions), @@ -866,6 +887,31 @@ generate({M,GenTOrV}, OutFile, EncodingRule, Options) -> asn1ct_table:delete(check_functions), Result. +setup_legacy_erlang_types(Opts) -> + F = case lists:member(legacy_erlang_types, Opts) of + false -> + case get_bit_string_format() of + bitstring -> + false; + compact -> + legacy_forced_info(compact_bit_string), + true; + legacy -> + legacy_forced_info(legacy_bit_string), + true + end; + true -> + true + end, + put(use_legacy_erlang_types, F). + +legacy_forced_info(Opt) -> + io:format("Info: The option 'legacy_erlang_types' " + "is implied by the '~s' option.\n", [Opt]). + +use_legacy_types() -> + get(use_legacy_erlang_types). + setup_bit_string_format(Opts) -> Format = case {lists:member(compact_bit_string, Opts), lists:member(legacy_bit_string, Opts)} of @@ -1011,7 +1057,7 @@ get_file_list1(Stream,Dir,Includes,Acc) -> Ret = io:get_line(Stream,''), case Ret of eof -> - file:close(Stream), + ok = file:close(Stream), lists:reverse(Acc); FileName -> SuffixedNameList = @@ -1072,6 +1118,7 @@ remove_asn_flags(Options) -> X /= optimize, X /= compact_bit_string, X /= legacy_bit_string, + X /= legacy_erlang_types, X /= debug, X /= asn1config, X /= record_name_prefix]. @@ -1896,8 +1943,9 @@ read_config_file(ModuleName) -> Includes = [I || {i,I} <- Options], read_config_file1(ModuleName,Includes); {error,Reason} -> - file:format_error(Reason), - throw({error,{"error reading asn1 config file",Reason}}) + Error = "error reading asn1 config file: " ++ + file:format_error(Reason), + throw({error,Error}) end. read_config_file1(ModuleName,[]) -> case filename:extension(ModuleName) of @@ -1915,8 +1963,9 @@ read_config_file1(ModuleName,[H|T]) -> {error,enoent} -> read_config_file1(ModuleName,T); {error,Reason} -> - file:format_error(Reason), - throw({error,{"error reading asn1 config file",Reason}}) + Error = "error reading asn1 config file: " ++ + file:format_error(Reason), + throw({error,Error}) end. get_config_info(CfgList,InfoType) -> @@ -2392,6 +2441,10 @@ verbose(Format, Args, S) -> ok end. +format_error({write_error,File,Reason}) -> + io_lib:format("writing output file ~s failed: ~s", + [File,file:format_error(Reason)]). + is_error(S) when is_record(S, state) -> is_error(S#state.options); is_error(O) -> diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index 0a13801e08..5d8740b92e 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -91,7 +91,7 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> save_asn1db_uptodate(S,S#state.erule,S#state.mname), put(top_module,S#state.mname), - _ = checkp(S, ParameterizedTypes), %must do this before the templates are used + ParamError = checkp(S, ParameterizedTypes), %must do this before the templates are used %% table to save instances of parameterized objects,object sets asn1ct_table:new(parameterized_objects), @@ -160,8 +160,10 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> Exporterror = check_exports(S,S#state.module), ImportError = check_imports(S,S#state.module), - case {Terror3,Verror5,Cerror,Oerror,Exporterror,ImportError} of - {[],[],[],[],[],[]} -> + AllErrors = lists:flatten([ParamError,Terror3,Verror5,Cerror, + Oerror,Exporterror,ImportError]), + case AllErrors of + [] -> ContextSwitchTs = context_switch_in_spec(), InstanceOf = instance_of_in_spec(S#state.mname), NewTypes = lists:subtract(Types,AddClasses) ++ ContextSwitchTs @@ -175,8 +177,7 @@ check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) -> lists:subtract(NewObjects,ExclO)++InlinedObjects, lists:subtract(NewObjectSets,ExclOS)++ParObjectSetNames}}; _ -> - {error,lists:flatten([Terror3,Verror5,Cerror, - Oerror,Exporterror,ImportError])} + {error,AllErrors} end. context_switch_in_spec() -> @@ -270,46 +271,30 @@ check_exports(S,Module = #module{}) -> end end. -check_imports(S,Module = #module{ }) -> - case Module#module.imports of - {imports,[]} -> - []; - {imports,ImportList} when is_list(ImportList) -> - check_imports2(S,ImportList,[]); - _ -> - [] - end. -check_imports2(_S,[],Acc) -> +check_imports(S, #module{imports={imports,Imports}}) -> + check_imports_1(S, Imports, []). + +check_imports_1(_S, [], Acc) -> Acc; -check_imports2(S,[#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs],Acc) -> - NameOfDef = - fun(#'Externaltypereference'{type=N}) -> N; - (#'Externalvaluereference'{value=N}) -> N - end, - Module = NameOfDef(ModuleRef), - Refs = [{M,R}||{{M,_},R} <- [{catch get_referenced_type(S,Ref),Ref}||Ref <- Imports]], - {Illegal,Other} = lists:splitwith(fun({error,_}) -> true;(_) -> false end, - Refs), - ChainedRefs = [R||{M,R} <- Other, M =/= Module], - IllegalRefs = [R||{error,R} <- Illegal] ++ - [R||{M,R} <- ChainedRefs, - ok =/= chained_import(S,Module,M,NameOfDef(R))], - ReportError = - fun(Ref) -> - NewS=S#state{type=Ref,tname=NameOfDef(Ref)}, - error({import,"imported undefined entity",NewS}) - end, - check_imports2(S,SFMs,[ReportError(Err)||Err <- IllegalRefs]++Acc). +check_imports_1(S, [#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs], Acc0) -> + Module = name_of_def(ModuleRef), + Refs0 = [{catch get_referenced_type(S, Ref),Ref} || Ref <- Imports], + Refs = [{M,R} || {{M,_},R} <- Refs0], + {Illegal,Other} = lists:splitwith(fun({error,_}) -> true; + (_) -> false + end, Refs), + ChainedRefs = [R || {M,R} <- Other, M =/= Module], + IllegalRefs = [R || {error,R} <- Illegal] ++ + [R || {M,R} <- ChainedRefs, + ok =/= chained_import(S, Module, M, name_of_def(R))], + Acc = [return_asn1_error(S, Ref, {undefined_import,name_of_def(Ref),Module}) || + Ref <- IllegalRefs] ++ Acc0, + check_imports_1(S, SFMs, Acc). chained_import(S,ImpMod,DefMod,Name) -> %% Name is a referenced structure that is not defined in ImpMod, %% but must be present in the Imports list of ImpMod. The chain of %% imports of Name must end in DefMod. - NameOfDef = - fun(#'Externaltypereference'{type=N}) -> N; - (#'Externalvaluereference'{value=N}) -> N; - (Other) -> Other - end, GetImports = fun(_M_) -> case asn1_db:dbget(_M_,'MODULE') of @@ -321,9 +306,9 @@ chained_import(S,ImpMod,DefMod,Name) -> FindNameInImports = fun([],N,_) -> {no_mod,N}; ([#'SymbolsFromModule'{symbols=Imports,module=ModuleRef}|SFMs],N,F) -> - case [NameOfDef(X)||X <- Imports, NameOfDef(X) =:= N] of + case [name_of_def(X) || X <- Imports, name_of_def(X) =:= N] of [] -> F(SFMs,N,F); - [N] -> {NameOfDef(ModuleRef),N} + [N] -> {name_of_def(ModuleRef),N} end end, case GetImports(ImpMod) of @@ -565,14 +550,10 @@ check_class(S = #state{mname=M,tname=T},ClassSpec) #objectclass{fields=Def}; % in case of recursive definitions Tref = #'Externaltypereference'{type=TName} -> {MName,RefType} = get_referenced_type(S,Tref), - case is_class(S,RefType) of - true -> - NewState = update_state(S#state{type=RefType, - tname=TName},MName), - check_class(NewState,get_class_def(S,RefType)); - _ -> - error({class,{internal_error,RefType},S}) - end; + #classdef{} = CD = get_class_def(S, RefType), + NewState = update_state(S#state{type=RefType, + tname=TName}, MName), + check_class(NewState, CD); {pt,ClassRef,Params} -> %% parameterized class {_,PClassDef} = get_referenced_type(S,ClassRef), @@ -966,6 +947,8 @@ prepare_objset(ObjDef={object,definedsyntax,_ObjFields}) -> {set,[ObjDef],false}; prepare_objset({ObjDef=#type{},Ext}) when is_list(Ext) -> {set,[ObjDef|Ext],true}; +prepare_objset({#type{}=Type,#type{}=Ext}) -> + {set,[Type,Ext],true}; prepare_objset(Ret) -> Ret. @@ -1293,10 +1276,25 @@ get_fieldname_element(_S,Def,[{_RefType,_FieldName}|_RestFName]) check_fieldname_element(S,{value,{_,Def}}) -> check_fieldname_element(S,Def); -check_fieldname_element(S,TDef) when is_record(TDef,typedef) -> - check_type(S,TDef,TDef#typedef.typespec); -check_fieldname_element(S,VDef) when is_record(VDef,valuedef) -> - check_value(S,VDef); +check_fieldname_element(S, #typedef{typespec=Ts}=TDef) -> + case Ts of + #'Object'{} -> + check_object(S, TDef, Ts); + _ -> + check_type(S, TDef, Ts) + end; +check_fieldname_element(S, #valuedef{}=VDef) -> + try + check_value(S, VDef) + catch + throw:{objectdef} -> + #valuedef{checked=C,pos=Pos,name=N,type=Type, + value=Def} = VDef, + ClassName = Type#type.def, + NewSpec = #'Object'{classname=ClassName,def=Def}, + NewDef = #typedef{checked=C,pos=Pos,name=N,typespec=NewSpec}, + check_fieldname_element(S, NewDef) + end; check_fieldname_element(S,Eref) when is_record(Eref,'Externaltypereference'); is_record(Eref,'Externalvaluereference') -> @@ -1567,13 +1565,13 @@ check_defaultfields(S, Fields, ClassFields) -> [] -> ok; [_|_]=Invalid -> - throw(asn1_error(S, T, {invalid_fields,Invalid,Obj})) + asn1_error(S, T, {invalid_fields,Invalid,Obj}) end, case ordsets:subtract(Mandatory, Present) of [] -> check_defaultfields_1(S, Fields, ClassFields, []); [_|_]=Missing -> - throw(asn1_error(S, T, {missing_mandatory_fields,Missing,Obj})) + asn1_error(S, T, {missing_mandatory_fields,Missing,Obj}) end. check_defaultfields_1(_S, [], _ClassFields, Acc) -> @@ -1819,12 +1817,10 @@ convert_to_defaultfield(S,ObjFieldName,[OFS|RestOFS],CField)-> FieldName); ValSetting = #valuedef{} -> ValSetting; - ValSetting = {'CHOICE',{Alt,_ChVal}} when is_atom(Alt) -> - #valuedef{type=element(3,CField), - value=ValSetting, - module=S#state.mname}; ValSetting -> - #identifier{val=ValSetting} + #valuedef{type=element(3,CField), + value=ValSetting, + module=S#state.mname} end, ?dbg("fixedtypevaluefield ValRef: ~p~n",[ValRef]), case ValRef of @@ -2308,22 +2304,23 @@ validate_oid(_, S, OID, [Id|Vrest], Acc) error({value, {"illegal "++to_string(OID),[Id,Vrest],Acc}, S}) end end; -validate_oid(_, S, OID, [{Atom,Value}],[]) +validate_oid(_, S, OID, [{#seqtag{module=Mod,val=Atom},Value}], []) when is_atom(Atom),is_integer(Value) -> %% this case when an OBJECT IDENTIFIER value has been parsed as a %% SEQUENCE value - Rec = #'Externalvaluereference'{module=S#state.mname, + Rec = #'Externalvaluereference'{module=Mod, value=Atom}, validate_objectidentifier1(S, OID, [Rec,Value]); -validate_oid(_, S, OID, [{Atom,EVRef}],[]) +validate_oid(_, S, OID, [{#seqtag{module=Mod,val=Atom},EVRef}], []) when is_atom(Atom),is_record(EVRef,'Externalvaluereference') -> %% this case when an OBJECT IDENTIFIER value has been parsed as a %% SEQUENCE value OTP-4354 - Rec = #'Externalvaluereference'{module=EVRef#'Externalvaluereference'.module, + Rec = #'Externalvaluereference'{module=Mod, value=Atom}, validate_objectidentifier1(S, OID, [Rec,EVRef]); -validate_oid(_, S, OID, [Atom|Rest],Acc) when is_atom(Atom) -> - Rec = #'Externalvaluereference'{module=S#state.mname, +validate_oid(_, S, OID, [#seqtag{module=Mod,val=Atom}|Rest], Acc) + when is_atom(Atom) -> + Rec = #'Externalvaluereference'{module=Mod, value=Atom}, validate_oid(true,S, OID, [Rec|Rest],Acc); validate_oid(_, S, OID, V, Acc) -> @@ -2464,7 +2461,7 @@ normalize_value(S0, Type, {'DEFAULT',Value}, NameList) -> {'BIT STRING',CType,_} -> normalize_bitstring(S,Value,CType); {'OCTET STRING',CType,_} -> - normalize_octetstring(S,Value,CType); + normalize_octetstring(S0, Value, CType); {'NULL',_CType,_} -> %%normalize_null(Value); 'NULL'; @@ -2574,6 +2571,18 @@ normalize_bitstring(S, Value, Type)-> Bs end. +hstring_to_binary(L) -> + byte_align(hstring_to_bitstring(L)). + +bstring_to_binary(L) -> + byte_align(bstring_to_bitstring(L)). + +byte_align(Bs) -> + case bit_size(Bs) rem 8 of + 0 -> Bs; + N -> <<Bs/bitstring,0:(8-N)>> + end. + hstring_to_bitstring(L) -> << <<(hex_to_int(D)):4>> || D <- L >>. @@ -2592,59 +2601,19 @@ hex_to_int(D) when $A =< D, D =< $F -> D - ($A - 10). normalize_octetstring(S,Value,CType) -> case Value of {bstring,String} -> - bstring_to_octetlist(String); + bstring_to_binary(String); {hstring,String} -> - hstring_to_octetlist(String); + hstring_to_binary(String); Rec when is_record(Rec,'Externalvaluereference') -> get_normalized_value(S,Value,CType, fun normalize_octetstring/3,[]); {Name,String} when is_atom(Name) -> normalize_octetstring(S,String,CType); - List when is_list(List) -> - %% check if list elements are valid octet values - lists:map(fun([])-> ok; - (H)when H > 255-> - asn1ct:warning("not legal octet value ~p in OCTET STRING, ~p~n", - [H,List],S, - "not legal octet value ~p in OCTET STRING"); - (_)-> ok - end, List), - List; - Other -> - asn1ct:warning("unknown default value ~p~n",[Other],S, - "unknown default value"), - Value + _ -> + Item = S#state.value, + asn1_error(S, Item, illegal_octet_string_value) end. - -bstring_to_octetlist([]) -> - []; -bstring_to_octetlist([H|T]) when H == $0 ; H == $1 -> - bstring_to_octetlist(T,6,[(H - $0) bsl 7]). -bstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H == $0; H == $1 -> - bstring_to_octetlist(T, 7, [0,Hacc + (H -$0)| Tacc]); -bstring_to_octetlist([H|T],BSL,[Hacc|Tacc]) when H == $0; H == $1 -> - bstring_to_octetlist(T, BSL-1, [Hacc + ((H - $0) bsl BSL)| Tacc]); -bstring_to_octetlist([],7,[0|Acc]) -> - lists:reverse(Acc); -bstring_to_octetlist([],_,Acc) -> - lists:reverse(Acc). - -hstring_to_octetlist([]) -> - []; -hstring_to_octetlist(L) -> - hstring_to_octetlist(L,4,[]). -hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $A, H =< $F -> - hstring_to_octetlist(T,4,[Hacc + (H - $A + 10)|Tacc]); -hstring_to_octetlist([H|T],BSL,Acc) when H >= $A, H =< $F -> - hstring_to_octetlist(T,0,[(H - $A + 10) bsl BSL|Acc]); -hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $0; H =< $9 -> - hstring_to_octetlist(T,4,[Hacc + (H - $0)|Tacc]); -hstring_to_octetlist([H|T],BSL,Acc) when H >= $0; H =< $9 -> - hstring_to_octetlist(T,0,[(H - $0) bsl BSL|Acc]); -hstring_to_octetlist([],_,Acc) -> - lists:reverse(Acc). - normalize_objectidentifier(S, Value) -> {ok,Val} = validate_objectidentifier(S, o_id, Value, []), Val. @@ -2659,18 +2628,21 @@ normalize_objectdescriptor(Value) -> normalize_real(Value) -> Value. -normalize_enumerated(S, Id, {Base,Ext}) -> +normalize_enumerated(S, Id0, NNL) -> + {Id,_} = lookup_enum_value(S, Id0, NNL), + Id. + +lookup_enum_value(S, Id, {Base,Ext}) -> %% Extensible ENUMERATED. - normalize_enumerated(S, Id, Base++Ext); -normalize_enumerated(S, #'Externalvaluereference'{value=Id}, - NamedNumberList) -> - normalize_enumerated(S, Id, NamedNumberList); -normalize_enumerated(S, Id, NamedNumberList) when is_atom(Id) -> - case lists:keymember(Id, 1, NamedNumberList) of - true -> - Id; + lookup_enum_value(S, Id, Base++Ext); +lookup_enum_value(S, #'Externalvaluereference'{value=Id}, NNL) -> + lookup_enum_value(S, Id, NNL); +lookup_enum_value(S, Id, NNL) when is_atom(Id) -> + case lists:keyfind(Id, 1, NNL) of + {_,_}=Ret -> + Ret; false -> - throw(asn1_error(S, S#state.value, {undefined,Id})) + asn1_error(S, S#state.value, {undefined,Id}) end. normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when is_atom(C) -> @@ -2730,20 +2702,20 @@ normalize_set(S,Value,Components,NameList) -> normalized_record('SET',S,SortedVal,Components,NameList) end. -sort_value(Components,Value) -> - ComponentNames = lists:map(fun(#'ComponentType'{name=Cname}) -> Cname end, - Components), - sort_value1(ComponentNames,Value,[]). -sort_value1(_,V=#'Externalvaluereference'{},_) -> - %% sort later, get the value in normalize_seq_or_set - V; -sort_value1([N|Ns],Value,Acc) -> - case lists:keysearch(N,1,Value) of - {value,V} ->sort_value1(Ns,Value,[V|Acc]); - _ -> sort_value1(Ns,Value,Acc) - end; -sort_value1([],_,Acc) -> - lists:reverse(Acc). +sort_value(Components, Value0) when is_list(Value0) -> + {Keys0,_} = lists:mapfoldl(fun(#'ComponentType'{name=N}, I) -> + {{N,I},I+1} + end, 0, Components), + Keys = gb_trees:from_orddict(orddict:from_list(Keys0)), + Value1 = [{case gb_trees:lookup(N, Keys) of + {value,K} -> K; + none -> 'end' + end,Pair} || {#seqtag{val=N},_}=Pair <- Value0], + Value = lists:sort(Value1), + [Pair || {_,Pair} <- Value]; +sort_value(_Components, #'Externalvaluereference'{}=Value) -> + %% Sort later. + Value. sort_val_if_set(['SET'|_],Val,Type) -> sort_value(Type,Val); @@ -2776,9 +2748,9 @@ is_record_normalized(_S,Name,Value,NumComps) when is_tuple(Value) -> is_record_normalized(_,_,_,_) -> false. -normalize_seq_or_set(SorS,S,[{Cname,V}|Vs], +normalize_seq_or_set(SorS, S, [{#seqtag{val=Cname},V}|Vs], [#'ComponentType'{name=Cname,typespec=TS}|Cs], - NameList,Acc) -> + NameList, Acc) -> NewNameList = case TS#type.def of #'Externaltypereference'{type=TName} -> @@ -2956,8 +2928,7 @@ get_canonic_type(S,Type,NameList) -> check_ptype(S,Type,Ts) when is_record(Ts,type) -> - %Tag = Ts#type.tag, - %Constr = Ts#type.constraint, + check_formal_parameters(S, Type#ptypedef.args), Def = Ts#type.def, NewDef= case Def of @@ -2983,6 +2954,16 @@ check_ptype(S,Type,Ts) when is_record(Ts,type) -> check_ptype(_S,_PTDef,Ts) when is_record(Ts,objectclass) -> throw({asn1_param_class,Ts}). +check_formal_parameters(S, Args) -> + _ = [check_formal_parameter(S, A) || A <- Args], + ok. + +check_formal_parameter(_, {_,_}) -> + ok; +check_formal_parameter(_, #'Externaltypereference'{}) -> + ok; +check_formal_parameter(S, #'Externalvaluereference'{value=Name}=Ref) -> + asn1_error(S, Ref, {illegal_typereference,Name}). % check_type(S,Type,ObjSpec={{objectclassname,_},_}) -> % check_class(S,ObjSpec); @@ -3030,9 +3011,9 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) -> {TmpRefMod,TmpRefDef} -> {TmpRefMod,TmpRefDef,false} end, - case is_class(S,RefTypeDef) of - true -> throw({asn1_class,RefTypeDef}); - _ -> ok + case get_class_def(S, RefTypeDef) of + none -> ok; + #classdef{} -> throw({asn1_class,RefTypeDef}) end, Ct = TestFun(Ext), {RefType,ExtRef} = @@ -3105,12 +3086,11 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) -> Ct=maybe_illicit_implicit_tag(open_type,Tag), TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct}; 'INTEGER' -> - check_integer(S,[],Constr), TempNewDef#newt{tag= merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))}; {'INTEGER',NamedNumberList} -> - TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList,Constr)}, + TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList)}, tag= merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))}; 'REAL' -> @@ -3118,8 +3098,7 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) -> TempNewDef#newt{tag=merge_tags(Tag,?TAG_PRIMITIVE(?N_REAL))}; {'BIT STRING',NamedNumberList} -> - NewL = check_bitstring(S,NamedNumberList,Constr), -%% erlang:display({asn1ct_check,NamedNumberList,NewL}), + NewL = check_bitstring(S, NamedNumberList), TempNewDef#newt{type={'BIT STRING',NewL}, tag= merge_tags(Tag,?TAG_PRIMITIVE(?N_BIT_STRING))}; @@ -3415,23 +3394,17 @@ get_type_from_object(S,Object,TypeField) ObjSpec = check_object(S,ObjectDef,ObjectDef#typedef.typespec), get_fieldname_element(S,ObjectDef#typedef{typespec=ObjSpec},TypeField). -is_class(_S,#classdef{}) -> - true; -is_class(S,#typedef{typespec=#type{def=Eref}}) - when is_record(Eref,'Externaltypereference')-> - is_class(S,Eref); -is_class(S,Eref) when is_record(Eref,'Externaltypereference')-> - {_,NextDef} = get_referenced_type(S,Eref), - is_class(S,NextDef); -is_class(_,_) -> - false. - -get_class_def(_S,CD=#classdef{}) -> +%% get_class_def(S, Type) -> #classdef{} | 'none'. +get_class_def(S, #typedef{typespec=#type{def=#'Externaltypereference'{}=Eref}}) -> + {_,NextDef} = get_referenced_type(S, Eref), + get_class_def(S, NextDef); +get_class_def(S, #'Externaltypereference'{}=Eref) -> + {_,NextDef} = get_referenced_type(S, Eref), + get_class_def(S, NextDef); +get_class_def(_S, #classdef{}=CD) -> CD; -get_class_def(S,#typedef{typespec=#type{def=Eref}}) - when is_record(Eref,'Externaltypereference') -> - {_,NextDef} = get_referenced_type(S,Eref), - get_class_def(S,NextDef). +get_class_def(_S, _) -> + none. maybe_illicit_implicit_tag(Kind,Tag) -> case Tag of @@ -3638,109 +3611,54 @@ match_args(_,_, _, _) -> %% categorize_arg(S,FormalArg,ActualArg) -> {FormalArg,CatgorizedActualArg} %% categorize_arg(S,{Governor,Param},ActArg) -> - case {governor_category(S,Governor),parameter_name_style(Param,ActArg)} of -%% {absent,beginning_uppercase} -> %% a type -%% categorize(S,type,ActArg); - {type,beginning_lowercase} -> %% a value - categorize(S,value,Governor,ActArg); - {type,beginning_uppercase} -> %% a value set - categorize(S,value_set,ActArg); -%% {absent,entirely_uppercase} -> %% a class -%% categorize(S,class,ActArg); + case {governor_category(S, Governor),parameter_name_style(Param)} of + {type,beginning_lowercase} -> %a value + categorize(S, value, Governor, ActArg); + {type,beginning_uppercase} -> %a value set + categorize(ActArg); {{class,ClassRef},beginning_lowercase} -> - categorize(S,object,ActArg,ClassRef); + categorize(S, object, ActArg, ClassRef); {{class,ClassRef},beginning_uppercase} -> - categorize(S,object_set,ActArg,ClassRef); - _ -> - [ActArg] + categorize(S, object_set, ActArg, ClassRef) end; -categorize_arg(S,FormalArg,ActualArg) -> - %% governor is absent => a type or a class - case FormalArg of - #'Externaltypereference'{type=Name} -> - case is_class_name(Name) of - true -> - categorize(S,class,ActualArg); - _ -> - categorize(S,type,ActualArg) - end; - FA -> - throw({error,{unexpected_formal_argument,FA}}) - end. - -governor_category(S,#type{def=Eref}) - when is_record(Eref,'Externaltypereference') -> - governor_category(S,Eref); -governor_category(_S,#type{}) -> +categorize_arg(_S, _FormalArg, ActualArg) -> + %% Governor is absent -- must be a type or a class. We have already + %% checked that the FormalArg begins with an uppercase letter. + categorize(ActualArg). + +%% governor_category(S, Item) -> type | {class,#'Externaltypereference'{}} +%% Determine whether Item is a type or a class. +governor_category(S, #type{def=#'Externaltypereference'{}=Eref}) -> + governor_category(S, Eref); +governor_category(_S, #type{}) -> type; -governor_category(S,Ref) when is_record(Ref,'Externaltypereference') -> - case is_class(S,Ref) of - true -> - {class,Ref}; - _ -> +governor_category(S, #'Externaltypereference'{}=Ref) -> + case get_class_def(S, Ref) of + #classdef{pos=Pos,module=Mod,name=Name} -> + {class,#'Externaltypereference'{pos=Pos,module=Mod,type=Name}}; + none -> type - end; -governor_category(_,Class) - when Class == 'TYPE-IDENTIFIER'; Class == 'ABSTRACT-SYNTAX' -> - class. -%% governor_category(_,_) -> -%% absent. + end. %% parameter_name_style(Param,Data) -> Result %% gets the Parameter and the name of the Data and if it exists tells %% whether it begins with a lowercase letter or is partly or entirely %% spelled with uppercase letters. Otherwise returns undefined %% -parameter_name_style(_,#'Externaltypereference'{type=Name}) -> - name_category(Name); -parameter_name_style(_,#'Externalvaluereference'{value=Name}) -> - name_category(Name); -parameter_name_style(_,{valueset,_}) -> - %% It is a object set or value set +parameter_name_style(#'Externaltypereference'{}) -> beginning_uppercase; -parameter_name_style(#'Externalvaluereference'{},_) -> - beginning_lowercase; -parameter_name_style(#'Externaltypereference'{type=Name},_) -> - name_category(Name); -parameter_name_style(_,_) -> - undefined. - -name_category(Atom) when is_atom(Atom) -> - name_category(atom_to_list(Atom)); -name_category([H|T]) -> - case is_lowercase(H) of - true -> - beginning_lowercase; - _ -> - case is_class_name(T) of - true -> - entirely_uppercase; - _ -> - beginning_uppercase - end - end; -name_category(_) -> - undefined. +parameter_name_style(#'Externalvaluereference'{}) -> + beginning_lowercase. is_lowercase(X) when X >= $A,X =< $W -> false; is_lowercase(_) -> true. - -is_class_name(Name) when is_atom(Name) -> - is_class_name(atom_to_list(Name)); -is_class_name(Name) -> - case [X||X <- Name, X >= $a,X =< $w] of - [] -> - true; - _ -> - false - end. -%% categorize(S,Category,Parameter) -> CategorizedParameter +%% categorize(Parameter) -> CategorizedParameter %% If Parameter has an abstract syntax of another category than %% Category, transform it to a known syntax. -categorize(_S,type,{object,_,Type}) -> +categorize({object,_,Type}) -> %% One example of this case is an object with a parameterized type %% having a locally defined type as parameter. Def = fun(D = #type{}) -> @@ -3752,11 +3670,12 @@ categorize(_S,type,{object,_,Type}) -> D end, [Def(X)||X<-Type]; -categorize(_S,type,Def) when is_record(Def,type) -> +categorize(#type{}=Def) -> [#typedef{name = new_reference_name("type_argument"), typespec = Def#type{inlined=yes}}]; -categorize(_,_,Def) -> +categorize(Def) -> [Def]. + categorize(S,object_set,Def,ClassRef) -> NewObjSetSpec = check_object(S,Def,#'ObjectSet'{class = ClassRef, @@ -3803,8 +3722,9 @@ resolv_value(S,Val) -> resolv_value1(S,Id). resolv_value1(S, ERef = #'Externalvaluereference'{value=Name}) -> - case catch resolve_namednumber(S,S#state.type,Name) of - V when is_integer(V) -> V; + case catch resolve_namednumber(S, S#state.type, Name) of + V when is_integer(V) -> + V; _ -> case get_referenced_type(S,ERef) of {Err,_Reason} when Err == error; Err == 'EXIT' -> @@ -3857,21 +3777,20 @@ resolve_value_from_object(S,Object,FieldName) -> end. - resolve_namednumber(S,#typedef{typespec=Type},Name) -> case Type#type.def of {'ENUMERATED',NameList} -> - NamedNumberList=check_enumerated(S,NameList,Type#type.constraint), - N = normalize_enumerated(S,Name,NamedNumberList), - {value,{_,V}} = lists:keysearch(N,1,NamedNumberList), - V; + resolve_namednumber_1(S, Name, NameList, Type); {'INTEGER',NameList} -> - NamedNumberList = check_enumerated(S,NameList,Type#type.constraint), - {value,{_,V}} = lists:keysearch(Name,1,NamedNumberList), - V; + resolve_namednumber_1(S, Name, NameList, Type); _ -> not_enumerated end. + +resolve_namednumber_1(S, Name, NameList, Type) -> + NamedNumberList = check_enumerated(S, NameList, Type#type.constraint), + {_,N} = lookup_enum_value(S, Name, NamedNumberList), + N. check_constraints(S,[{'ContainedSubtype',Type} | Rest], Acc) -> {RefMod,CTDef} = get_referenced_type(S,Type#type.def), @@ -3952,9 +3871,9 @@ check_constraint(S,{simpletable,Type}) -> #'Externaltypereference'{} -> ERef = check_externaltypereference(S,C), {simpletable,ERef#'Externaltypereference'.type}; - #type{def=#'Externaltypereference'{type=T}} -> - check_externaltypereference(S,C#type.def), - {simpletable,T}; + #type{def=#'Externaltypereference'{}=ExtTypeRef} -> + ERef = check_externaltypereference(S, ExtTypeRef), + {simpletable,ERef#'Externaltypereference'.type}; {valueset,#type{def=ERef=#'Externaltypereference'{}}} -> % this is an object set {_,TDef} = get_referenced_type(S,ERef), case TDef#typedef.typespec of @@ -4589,55 +4508,43 @@ check_reference(S,#'Externaltypereference'{pos=Pos,module=Emod,type=Name}) -> #'Externaltypereference'{pos=Pos,module=ModName,type=Name} end. +get_referenced_type(S, T) -> + case do_get_referenced_type(S, T) of + {_,#type{def=#'Externaltypereference'{}=ERef}} -> + get_referenced_type(S, ERef); + {_,#type{def=#'Externalvaluereference'{}=VRef}} -> + get_referenced_type(S, VRef); + {_,_}=Res -> + Res + end. -get_referenced_type(S,Ext) when is_record(Ext,'Externaltypereference') -> - case match_parameters(S,Ext, S#state.parameters) of - Ext -> - #'Externaltypereference'{pos=Pos,module=Emod,type=Etype} = Ext, - case S#state.mname of - Emod -> % a local reference in this module - get_referenced1(S,Emod,Etype,Pos); - _ ->% always when multi file compiling - case lists:member(Emod,S#state.inputmodules) of - true -> - get_referenced1(S,Emod,Etype,Pos); - false -> - get_referenced(S,Emod,Etype,Pos) - end - end; - ERef = #'Externaltypereference'{} -> - get_referenced_type(S,ERef); - Other -> - {undefined,Other} - end; -get_referenced_type(S=#state{mname=Emod}, - ERef=#'Externalvaluereference'{pos=P,module=Emod, - value=Eval}) -> - case match_parameters(S,ERef,S#state.parameters) of - ERef -> - get_referenced1(S,Emod,Eval,P); - OtherERef when is_record(OtherERef,'Externalvaluereference') -> - get_referenced_type(S,OtherERef); - Value -> - {Emod,Value} - end; -get_referenced_type(S,ERef=#'Externalvaluereference'{pos=Pos,module=Emod, - value=Eval}) -> - case match_parameters(S,ERef,S#state.parameters) of - ERef -> - case lists:member(Emod,S#state.inputmodules) of - true -> - get_referenced1(S,Emod,Eval,Pos); - false -> - get_referenced(S,Emod,Eval,Pos) - end; - OtherERef -> - get_referenced_type(S,OtherERef) - end; -get_referenced_type(S,#identifier{val=Name,pos=Pos}) -> - get_referenced1(S,undefined,Name,Pos); -get_referenced_type(_S,Type) -> - {undefined,Type}. +do_get_referenced_type(#state{parameters=Ps}=S, T0) -> + case match_parameters(S, T0, Ps) of + T0 -> + do_get_ref_type_1(S, T0); + T -> + do_get_referenced_type(S, T) + end. + +do_get_ref_type_1(S, #'Externaltypereference'{pos=P, + module=M, + type=T}) -> + do_get_ref_type_2(S, P, M, T); +do_get_ref_type_1(S, #'Externalvaluereference'{pos=P, + module=M, + value=V}) -> + do_get_ref_type_2(S, P, M, V); +do_get_ref_type_1(_, T) -> + {undefined,T}. + +do_get_ref_type_2(#state{mname=Current,inputmodules=Modules}=S, + Pos, M, T) -> + case M =:= Current orelse lists:member(M, Modules) of + true -> + get_referenced1(S, M, T, Pos); + false -> + get_referenced(S, M, T, Pos) + end. %% get_referenced/3 %% The referenced entity Ename may in case of an imported parameterized @@ -4936,73 +4843,46 @@ imported1(Name, end; imported1(_Name,[]) -> false. - -check_integer(_S,[],_C) -> +%% Check the named number list for an INTEGER or a BIT STRING. +check_named_number_list(_S, []) -> []; -check_integer(S,NamedNumberList,_C) -> - case [X || X <- NamedNumberList, tuple_size(X) =:= 2] of - NamedNumberList -> - %% An already checked integer with NamedNumberList - NamedNumberList; - _ -> - case check_unique(NamedNumberList,2) of - [] -> - check_int(S,NamedNumberList,[]); - L when is_list(L) -> - error({type,{duplicates,L},S}), - unchanged - end +check_named_number_list(_S, [{_,_}|_]=NNL) -> + %% The named number list has already been checked. + NNL; +check_named_number_list(S, NNL0) -> + %% Check that the names are unique. + T = S#state.type, + case check_unique(NNL0, 2) of + [] -> + NNL1 = [{Id,resolve_valueref(S, Val)} || {'NamedNumber',Id,Val} <- NNL0], + NNL = lists:keysort(2, NNL1), + case check_unique(NNL, 2) of + [] -> + NNL; + [Val|_] -> + asn1_error(S, T, {value_reused,Val}) + end; + [H|_] -> + asn1_error(S, T, {namelist_redefinition,H}) end. - -check_int(S,[{'NamedNumber',Id,Num}|T],Acc) when is_integer(Num) -> - check_int(S,T,[{Id,Num}|Acc]); -check_int(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) -> - Val = dbget_ex(S,S#state.mname,Name), - check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc); -check_int(S,[{'NamedNumber',Id,{'Externalvaluereference',_,Mod,Name}}|T],Acc) -> - Val = dbget_ex(S,Mod,Name), - check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc); -check_int(_S,[],Acc) -> - lists:keysort(2,Acc). +resolve_valueref(S, #'Externalvaluereference'{module=Mod,value=Name}) -> + dbget_ex(S, Mod, Name); +resolve_valueref(_, Val) when is_integer(Val) -> + Val. -check_real(_S,_Constr) -> - ok. +check_integer(S, NNL) -> + check_named_number_list(S, NNL). -check_bitstring(_S,[],_Constr) -> - []; -check_bitstring(S,NamedNumberList,_Constr) -> - case check_unique(NamedNumberList,2) of - [] -> - check_bitstr(S,NamedNumberList,[]); - L when is_list(L) -> - error({type,{duplicates,L},S}), - unchanged - end. +check_bitstring(S, NNL0) -> + NNL = check_named_number_list(S, NNL0), + _ = [asn1_error(S, S#state.type, {invalid_bit_number,Bit}) || + {_,Bit} <- NNL, Bit < 0], + NNL. -check_bitstr(S,[{'NamedNumber',Id,Num}|T],Acc)when is_integer(Num) -> - check_bitstr(S,T,[{Id,Num}|Acc]); -check_bitstr(S,[{'NamedNumber',Id,Name}|T],Acc) when is_atom(Name) -> -%%check_bitstr(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) -> -%% io:format("asn1ct_check:check_bitstr/3 hej hop ~w~n",[Name]), - Val = dbget_ex(S,S#state.mname,Name), -%% io:format("asn1ct_check:check_bitstr/3: ~w~n",[Val]), - check_bitstr(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc); -check_bitstr(S,[],Acc) -> - case check_unique(Acc,2) of - [] -> - lists:keysort(2,Acc); - L when is_list(L) -> - error({type,{duplicate_values,L},S}), - unchanged - end; -%% When a BIT STRING already is checked, for instance a COMPONENTS OF S -%% where S is a sequence that has a component that is a checked BS, the -%% NamedNumber list is a list of {atom(),integer()} elements. -check_bitstr(S,[El={Id,Num}|Rest],Acc) when is_atom(Id),is_integer(Num) -> - check_bitstr(S,Rest,[El|Acc]). - +check_real(_S,_Constr) -> + ok. %% Check INSTANCE OF %% check that DefinedObjectClass is of TYPE-IDENTIFIER class @@ -5013,20 +4893,16 @@ check_instance_of(S,DefinedObjectClass,Constraint) -> check_type_identifier(S,DefinedObjectClass), iof_associated_type(S,Constraint). - -check_type_identifier(_S,'TYPE-IDENTIFIER') -> - ok; -check_type_identifier(S,Eref=#'Externaltypereference'{}) -> - case get_referenced_type(S,Eref) of - {_,#classdef{name='TYPE-IDENTIFIER'}} -> ok; - {_,#classdef{typespec=NextEref}} - when is_record(NextEref,'Externaltypereference') -> - check_type_identifier(S,NextEref); +check_type_identifier(S, Eref=#'Externaltypereference'{type=Class}) -> + case get_referenced_type(S, Eref) of + {_,#classdef{name='TYPE-IDENTIFIER'}} -> + ok; + {_,#classdef{typespec=#'Externaltypereference'{}=NextEref}} -> + check_type_identifier(S, NextEref); {_,TD=#typedef{typespec=#type{def=#'Externaltypereference'{}}}} -> - check_type_identifier(S,(TD#typedef.typespec)#type.def); - Err -> - error({type,{"object set in type INSTANCE OF " - "not of class TYPE-IDENTIFIER",Eref,Err},S}) + check_type_identifier(S, (TD#typedef.typespec)#type.def); + _ -> + asn1_error(S, S#state.type, {illegal_instance_of,Class}) end. iof_associated_type(S,[]) -> @@ -5156,9 +5032,6 @@ check_enumerated(S,NamedNumberList,_Constr) -> %% the latter is returned if the ENUMERATION contains EXTENSIONMARK check_enum(S,[{'NamedNumber',Id,Num}|T],Acc1,Acc2,Root) when is_integer(Num) -> check_enum(S,T,[{Id,Num}|Acc1],Acc2,Root); -check_enum(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc1,Acc2,Root) -> - Val = dbget_ex(S,S#state.mname,Name), - check_enum(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc1,Acc2,Root); check_enum(S,['EXTENSIONMARK'|T],Acc1,Acc2,_Root) -> NewAcc2 = lists:keysort(2,Acc1), NewList = enum_number(lists:reverse(Acc2),NewAcc2,0,[],[]), @@ -6774,7 +6647,7 @@ storeindb(#state{mname=Module}=S, [H|T], Errors) -> storeindb(S, T, Errors); Prev -> PrevLine = asn1ct:get_pos_of_def(Prev), - {error,Error} = asn1_error(S, H, {already_defined,Name,PrevLine}), + Error = return_asn1_error(S, H, {already_defined,Name,PrevLine}), storeindb(S, T, [Error|Errors]) end; storeindb(_, [], []) -> @@ -6821,20 +6694,39 @@ findtypes_and_values([],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc) -> {lists:reverse(Tacc),lists:reverse(Vacc),lists:reverse(Pacc), lists:reverse(Cacc),lists:reverse(Oacc),lists:reverse(OSacc)}. -asn1_error(#state{mname=Where}, Item, Error) -> +return_asn1_error(#state{mname=Where}, Item, Error) -> Pos = asn1ct:get_pos_of_def(Item), - {error,{structured_error,{Where,Pos},?MODULE,Error}}. + {structured_error,{Where,Pos},?MODULE,Error}. + +asn1_error(S, Item, Error) -> + throw({error,return_asn1_error(S, Item, Error)}). format_error({already_defined,Name,PrevLine}) -> io_lib:format("the name ~p has already been defined at line ~p", [Name,PrevLine]); +format_error({illegal_instance_of,Class}) -> + io_lib:format("using INSTANCE OF on class '~s' is illegal, " + "because INSTANCE OF may only be used on the class TYPE-IDENTFIER", + [Class]); +format_error(illegal_octet_string_value) -> + "expecting a bstring or an hstring as value for an OCTET STRING"; +format_error({illegal_typereference,Name}) -> + io_lib:format("'~p' is used as a typereference, but does not start with an uppercase letter", [Name]); format_error({invalid_fields,Fields,Obj}) -> io_lib:format("invalid ~s in ~p", [format_fields(Fields),Obj]); +format_error({invalid_bit_number,Bit}) -> + io_lib:format("the bit number '~p' is invalid", [Bit]); format_error({missing_mandatory_fields,Fields,Obj}) -> io_lib:format("missing mandatory ~s in ~p", [format_fields(Fields),Obj]); +format_error({namelist_redefinition,Name}) -> + io_lib:format("the name '~s' can not be redefined", [Name]); format_error({undefined,Name}) -> io_lib:format("'~s' is referenced, but is not defined", [Name]); +format_error({undefined_import,Ref,Module}) -> + io_lib:format("'~s' is not exported from ~s", [Ref,Module]); +format_error({value_reused,Val}) -> + io_lib:format("the value '~p' is used more than once", [Val]); format_error(Other) -> io_lib:format("~p", [Other]). @@ -6850,14 +6742,6 @@ error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) -> Pos = Ref#'Externaltypereference'.pos, io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]), {error,{export,Pos,Mname,Typename,Msg}}; -error({import,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) -> - PosOfDef = - fun(#'Externaltypereference'{pos=P}) -> P; - (#'Externalvaluereference'{pos=P}) -> P - end, - Pos = PosOfDef(Ref), - io:format("asn1error:~p:~p:~p~n~p~n",[Pos,Mname,Typename,Msg]), - {error,{import,Pos,Mname,Typename,Msg}}; % error({type,{Msg1,Msg2},#state{mname=Mname,type=Type,tname=Typename}}) % when is_record(Type,typedef) -> % io:format("asn1error:~p:~p:~p ~p~n", @@ -7074,7 +6958,7 @@ include_default_class1(_,[]) -> include_default_class1(Module,[{Name,TS}|Rest]) -> case asn1_db:dbget(Module,Name) of undefined -> - C = #classdef{checked=true,name=Name, + C = #classdef{checked=true,module=Module,name=Name, typespec=TS}, asn1_db:dbput(Module,Name,C); _ -> ok @@ -7158,3 +7042,6 @@ check_fold(S, [H|T], Check) -> [Error|check_fold(S, T, Check)] end; check_fold(_, [], Check) when is_function(Check, 3) -> []. + +name_of_def(#'Externaltypereference'{type=N}) -> N; +name_of_def(#'Externalvaluereference'{value=N}) -> N. diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl index a38da8bcc2..5fadd0495a 100644 --- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl @@ -962,8 +962,7 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj) WhatKind = asn1ct_gen:type(InnerType), emit(IndDeep), emit(Assign), - gen_optormand_case(OptOrMand,Erules,TopType,Cname,Type,InnerType,WhatKind, - Element), + gen_optormand_case(OptOrMand, Erules, TopType, Cname, Type, Element), case {Type,asn1ct_gen:get_constraint(Type#type.constraint, componentrelation)} of % #type{constraint=[{tableconstraint_info,RefedFieldName}], @@ -1029,26 +1028,19 @@ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj) emit([nl,indent(7),"end"]) end. -gen_optormand_case(mandatory,_Erules,_TopType,_Cname,_Type,_InnerType,_WhatKind, - _Element) -> +gen_optormand_case(mandatory, _Erules, _TopType, _Cname, _Type, _Element) -> ok; -gen_optormand_case('OPTIONAL',Erules,_TopType,_Cname,_Type,_InnerType,_WhatKind, - Element) -> +gen_optormand_case('OPTIONAL', Erules, _TopType, _Cname, _Type, Element) -> emit([" case ",Element," of",nl]), emit([indent(9),"asn1_NOVALUE -> {", empty_lb(Erules),",0};",nl]), emit([indent(9),"_ ->",nl,indent(12)]); -gen_optormand_case({'DEFAULT',DefaultValue},Erules,TopType,Cname,Type, - InnerType,WhatKind,Element) -> +gen_optormand_case({'DEFAULT',DefaultValue}, Erules, _TopType, + _Cname, Type, Element) -> CurrMod = get(currmod), case catch lists:member(der,get(encoding_options)) of true -> - emit(" case catch "), - asn1ct_gen:gen_check_call(TopType,Cname,Type,InnerType, - WhatKind,{asis,DefaultValue}, - Element), - emit([" of",nl]), - emit([indent(12),"true -> {[],0};",nl]); + asn1ct_gen_check:emit(Type, DefaultValue, Element); _ -> emit([" case ",Element," of",nl]), emit([indent(9),"asn1_DEFAULT -> {", @@ -1063,10 +1055,9 @@ gen_optormand_case({'DEFAULT',DefaultValue},Erules,TopType,Cname,Type, emit([indent(9),{asis, DefaultValue}," -> {", empty_lb(Erules),",0};",nl]) - end - end, - emit([indent(9),"_ ->",nl,indent(12)]). - + end, + emit([indent(9),"_ ->",nl,indent(12)]) + end. gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) -> @@ -1210,11 +1201,11 @@ gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandC (Type#type.def)#'ObjectClassFieldType'.fieldname, [{Cname,RefedFieldName,asn1ct_gen:mk_var(asn1ct_name:curr(term)), asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}]; -gen_dec_call(InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,PrimOptOrMand, - OptOrMand,DecObjInf,_) -> +gen_dec_call(InnerType, _Erules, TopType, Cname, Type, BytesVar, + Tag, _PrimOptOrMand, _OptOrMand, DecObjInf,_) -> WhatKind = asn1ct_gen:type(InnerType), - gen_dec_call1(WhatKind,InnerType,Erules,TopType,Cname,Type,BytesVar,Tag, - PrimOptOrMand,OptOrMand), + gen_dec_call1(WhatKind, InnerType, TopType, Cname, + Type, BytesVar, Tag), case DecObjInf of {Cname,{_,OSet,_UniqueFName,ValIndex}} -> Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)), @@ -1226,8 +1217,9 @@ gen_dec_call(InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,PrimOptOrMand, ok end, []. -gen_dec_call1({primitive,bif},InnerType,Erules,TopType,Cname,Type,BytesVar, - Tag,OptOrMand,_) -> + +gen_dec_call1({primitive,bif}, InnerType, TopType, Cname, + Type, BytesVar, Tag) -> case {asn1ct:get_gen_state_field(namelist),InnerType} of {[{Cname,undecoded}|Rest],_} -> asn1ct:add_generated_refed_func({[Cname|TopType],undecoded, @@ -1236,11 +1228,10 @@ gen_dec_call1({primitive,bif},InnerType,Erules,TopType,Cname,Type,BytesVar, emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',", BytesVar,"}"]); _ -> - ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Type,BytesVar,Tag,[], - ?PRIMITIVE,OptOrMand) + ?ASN1CT_GEN_BER:gen_dec_prim(Type, BytesVar, Tag) end; -gen_dec_call1('ASN1_OPEN_TYPE',_InnerType,Erules,TopType,Cname,Type,BytesVar, - Tag,OptOrMand,_) -> +gen_dec_call1('ASN1_OPEN_TYPE', _InnerType, TopType, Cname, + Type, BytesVar, Tag) -> case {asn1ct:get_gen_state_field(namelist),Type#type.def} of {[{Cname,undecoded}|Rest],_} -> asn1ct:add_generated_refed_func({[Cname|TopType],undecoded, @@ -1249,15 +1240,12 @@ gen_dec_call1('ASN1_OPEN_TYPE',_InnerType,Erules,TopType,Cname,Type,BytesVar, emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',", BytesVar,"}"]); {_,#'ObjectClassFieldType'{type=OpenType}} -> - ?ASN1CT_GEN_BER:gen_dec_prim(Erules,#type{def=OpenType}, - BytesVar,Tag,[], - ?PRIMITIVE,OptOrMand); + ?ASN1CT_GEN_BER:gen_dec_prim(#type{def=OpenType}, + BytesVar, Tag); _ -> - ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Type,BytesVar,Tag,[], - ?PRIMITIVE,OptOrMand) + ?ASN1CT_GEN_BER:gen_dec_prim(Type, BytesVar, Tag) end; -gen_dec_call1(WhatKind,_,_Erules,TopType,Cname,Type,BytesVar, - Tag,_,_OptOrMand) -> +gen_dec_call1(WhatKind, _, TopType, Cname, Type, BytesVar, Tag) -> case asn1ct:get_gen_state_field(namelist) of [{Cname,undecoded}|Rest] -> asn1ct:add_generated_refed_func({[Cname|TopType],undecoded, diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index 4672f7edd3..a91404ed54 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -79,7 +79,7 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> [] end, Aligned = is_aligned(Erule), - Value0 = asn1ct_gen:mk_var(asn1ct_name:curr(val)), + Value0 = make_var(val), Optionals = optionals(to_textual_order(CompList)), ImmOptionals = [asn1ct_imm:per_enc_optional(Value0, Opt, Aligned) || Opt <- Optionals], @@ -87,7 +87,7 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> ExtImm = case Ext of {ext,ExtPos,NumExt} when NumExt > 0 -> gen_encode_extaddgroup(CompList), - Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), + Value = make_var(val), asn1ct_imm:per_enc_extensions(Value, ExtPos, NumExt, Aligned); _ -> @@ -106,19 +106,17 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> c_index=N, usedclassfield=UniqueFieldName, uniqueclassfield=UniqueFieldName, - valueindex=ValueIndex + valueindex=ValueIndex0 } -> %% N is index of attribute that determines constraint {Module,ObjSetName} = ObjectSet, #typedef{typespec=#'ObjectSet'{gen=Gen}} = asn1_db:dbget(Module, ObjSetName), case Gen of true -> - ObjectEncode = - asn1ct_gen:un_hyphen_var(lists:concat(['Obj',AttrN])), - El = make_element(N+1, asn1ct_gen:mk_var(asn1ct_name:curr(val))), - ValueMatch = value_match(ValueIndex, El), - ObjSetImm0 = [{assign,{var,ObjectEncode},ValueMatch}], - {{AttrN,ObjectEncode},ObjSetImm0}; + ValueIndex = ValueIndex0 ++ [{N+1,top}], + Val = make_var(val), + {ObjSetImm0,Dst} = enc_dig_out_value(ValueIndex, Val), + {{AttrN,Dst},ObjSetImm0}; false -> {false,[]} end; @@ -128,7 +126,7 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> %% when the simpletableattributes was at an outer %% level and the objfun has been passed through the %% function call - {{"got objfun through args","ObjFun"},[]}; + {{"got objfun through args",{var,"ObjFun"}},[]}; _ -> {false,[]} end @@ -136,7 +134,7 @@ gen_encode_constructed_imm(Erule, Typename, #type{}=D) -> ImmSetExt = case Ext of {ext,_Pos,NumExt2} when NumExt2 > 0 -> - asn1ct_imm:per_enc_extension_bit('Extensions', Aligned); + asn1ct_imm:per_enc_extension_bit({var,"Extensions"}, Aligned); {ext,_Pos,_} -> asn1ct_imm:per_enc_extension_bit([], Aligned); _ -> @@ -428,8 +426,7 @@ gen_dec_open_type(Erule, Val, {Xmod,Xtype}, LeadingAttr, emit([Term," = ",{asis,F},"(",TmpTerm,", ",Val,")"]). dec_objset_optional(N, {'DEFAULT',Val}) -> - dec_objset_optional_1(N, Val), - dec_objset_optional_1(N, asn1_DEFAULT); + dec_objset_optional_1(N, Val); dec_objset_optional(N, 'OPTIONAL') -> dec_objset_optional_1(N, asn1_NOVALUE); dec_objset_optional(_N, mandatory) -> ok. @@ -452,8 +449,13 @@ dec_objset_default(N, C, LeadingAttr, false) -> "{value,Bytes}," "{unique_name_and_value,",{asis,LeadingAttr},",Id}}}).",nl,nl]); dec_objset_default(N, _, _, true) -> - emit([{asis,N},"(Bytes, Id) ->",nl, - "Bytes.",nl,nl]). + emit([{asis,N},"(Bytes, Id) ->",nl| + case asn1ct:use_legacy_types() of + false -> + ["{asn1_OPENTYPE,Bytes}.",nl,nl]; + true -> + ["Bytes.",nl,nl] + end]). dec_objset_1(Erule, N, {Id,Obj}, RestFields, Typename) -> emit([{asis,N},"(Bytes, ",{asis,Id},") ->",nl]), @@ -540,7 +542,7 @@ gen_encode_choice_imm(Erule, TopType, #type{def={'CHOICE',CompList}}) -> Aligned = is_aligned(Erule), Cs = gen_enc_choice(Erule, TopType, CompList, Ext), [{assign,{expr,"{ChoiceTag,ChoiceVal}"},"Val"}| - asn1ct_imm:per_enc_choice('ChoiceTag', Cs, Aligned)]. + asn1ct_imm:per_enc_choice({var,"ChoiceTag"}, Cs, Aligned)]. gen_decode_choice(Erules,Typename,D) when is_record(D,type) -> asn1ct_name:start(), @@ -562,14 +564,14 @@ gen_encode_sof(Erule, Typename, SeqOrSetOf, D) -> gen_encode_sof_imm(Erule, Typename, SeqOrSetOf, #type{}=D) -> {_SeqOrSetOf,ComponentType} = D#type.def, Aligned = is_aligned(Erule), - Constructed_Suffix = - asn1ct_gen:constructed_suffix(SeqOrSetOf, - ComponentType#type.def), - Conttype = asn1ct_gen:get_inner(ComponentType#type.def), + CompType = ComponentType#type.def, + Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf, CompType), + Conttype = asn1ct_gen:get_inner(CompType), Currmod = get(currmod), Imm0 = case asn1ct_gen:type(Conttype) of {primitive,bif} -> - asn1ct_gen_per:gen_encode_prim_imm('Comp', ComponentType, Aligned); + asn1ct_gen_per:gen_encode_prim_imm({var,"Comp"}, + ComponentType, Aligned); {constructed,bif} -> TypeName = [Constructed_Suffix|Typename], Enc = enc_func(asn1ct_gen:list2name(TypeName)), @@ -577,17 +579,19 @@ gen_encode_sof_imm(Erule, Typename, SeqOrSetOf, #type{}=D) -> [{objfun,_}|_] -> [{var,"ObjFun"}]; _ -> [] end, - [{apply,Enc,[{var,"Comp"}|ObjArg]}]; + [{apply,{local,Enc,CompType}, + [{var,"Comp"}|ObjArg]}]; #'Externaltypereference'{module=Currmod,type=Ename} -> - [{apply,enc_func(Ename),[{var,"Comp"}]}]; + [{apply,{local,enc_func(Ename),CompType},[{var,"Comp"}]}]; #'Externaltypereference'{module=EMod,type=Ename} -> - [{apply,{EMod,enc_func(Ename)},[{var,"Comp"}]}]; + [{apply,{EMod,enc_func(Ename),CompType},[{var,"Comp"}]}]; 'ASN1_OPEN_TYPE' -> - asn1ct_gen_per:gen_encode_prim_imm('Comp', + asn1ct_gen_per:gen_encode_prim_imm({var,"Comp"}, #type{def='ASN1_OPEN_TYPE'}, Aligned) end, - asn1ct_imm:per_enc_sof('Val', D#type.constraint, 'Comp', Imm0, Aligned). + asn1ct_imm:per_enc_sof({var,"Val"}, D#type.constraint, 'Comp', + Imm0, Aligned). gen_decode_sof(Erules, Typename, SeqOrSetOf, #type{}=D) -> asn1ct_name:start(), @@ -871,8 +875,8 @@ gen_enc_components_call1(Erule,TopType, CanonicalNum -> CanonicalNum end, - Element0 = make_element(TermNo+1, asn1ct_gen:mk_var(asn1ct_name:curr(val))), - {Imm0,Element} = asn1ct_imm:enc_bind_var(Element0), + Val = make_var(val), + {Imm0,Element} = asn1ct_imm:enc_element(TermNo+1, Val), Imm1 = gen_enc_line_imm(Erule, TopType, Cname, Type, Element, DynamicEnc, Ext), Category = case {Prop,Ext} of {'OPTIONAL',_} -> @@ -906,26 +910,36 @@ def_values(#type{def=#'Externaltypereference'{module=Mod,type=Type}}, Def) -> #typedef{typespec=T} = asn1_db:dbget(Mod, Type), def_values(T, Def); def_values(#type{def={'BIT STRING',[]}}, Bs) when is_bitstring(Bs) -> - ListBs = [B || <<B:1>> <= Bs], - IntBs = lists:foldl(fun(B, A) -> - (A bsl 1) bor B - end, 0, lists:reverse(ListBs)), - Sz = bit_size(Bs), - Compact = case 8 - Sz rem 8 of - 8 -> - {0,Bs}; - Unused -> - {Unused,<<Bs:Sz/bits,0:Unused>>} - end, - [asn1_DEFAULT,Bs,Compact,ListBs,IntBs]; + case asn1ct:use_legacy_types() of + false -> + [asn1_DEFAULT,Bs]; + true -> + ListBs = [B || <<B:1>> <= Bs], + IntBs = lists:foldl(fun(B, A) -> + (A bsl 1) bor B + end, 0, lists:reverse(ListBs)), + Sz = bit_size(Bs), + Compact = case 8 - Sz rem 8 of + 8 -> + {0,Bs}; + Unused -> + {Unused,<<Bs:Sz/bits,0:Unused>>} + end, + [asn1_DEFAULT,Bs,Compact,ListBs,IntBs] + end; def_values(#type{def={'BIT STRING',[_|_]=Ns}}, List) when is_list(List) -> Bs = asn1ct_gen:named_bitstring_value(List, Ns), - ListBs = [B || <<B:1>> <= Bs], - IntBs = lists:foldl(fun(B, A) -> - (A bsl 1) bor B - end, 0, lists:reverse(ListBs)), - Args = [List,Bs,ListBs,IntBs], - {call,per_common,is_default_bitstring,Args}; + As = case asn1ct:use_legacy_types() of + false -> + [List,Bs]; + true -> + ListBs = [B || <<B:1>> <= Bs], + IntBs = lists:foldl(fun(B, A) -> + (A bsl 1) bor B + end, 0, lists:reverse(ListBs)), + [List,Bs,ListBs,IntBs] + end, + {call,per_common,is_default_bitstring,As}; def_values(#type{def={'INTEGER',Ns}}, Def) -> [asn1_DEFAULT,Def|case lists:keyfind(Def, 2, Ns) of false -> []; @@ -967,9 +981,9 @@ gen_enc_line_imm_1(Erule, TopType, Cname, Type, Element, DynamicEnc) -> CurrMod = get(currmod), case asn1ct_gen:type(Atype) of #'Externaltypereference'{module=CurrMod,type=EType} -> - [{apply,enc_func(EType),[{expr,Element}]}]; + [{apply,{local,enc_func(EType),Atype},[Element]}]; #'Externaltypereference'{module=Mod,type=EType} -> - [{apply,{Mod,enc_func(EType)},[{expr,Element}]}]; + [{apply,{Mod,enc_func(EType),Atype},[Element]}]; {primitive,bif} -> asn1ct_gen_per:gen_encode_prim_imm(Element, Type, Aligned); 'ASN1_OPEN_TYPE' -> @@ -988,9 +1002,9 @@ gen_enc_line_imm_1(Erule, TopType, Cname, Type, Element, DynamicEnc) -> Enc = enc_func(asn1ct_gen:list2name(NewTypename)), case {Type#type.tablecinf,DynamicEnc} of {[{objfun,_}|_R],{_,EncFun}} -> - [{apply,Enc,[{expr,Element},{var,EncFun}]}]; + [{apply,{local,Enc,Type},[Element,EncFun]}]; _ -> - [{apply,Enc,[{expr,Element}]}] + [{apply,{local,Enc,Type},[Element]}] end end end. @@ -1014,13 +1028,16 @@ enc_var_type_call(Erule, Name, RestFieldNames, {_,Key,Code} <- ObjSet1], ObjSet = lists:sort([P || {_,B}=P <- ObjSet2, B =/= none]), Key = erlang:md5(term_to_binary({encode,ObjSet,RestFieldNames,Extensible})), + Imm = enc_objset_imm(Erule, Name, ObjSet, RestFieldNames, Extensible), + Lambda = {lambda,[{var,"Val"},{var,"Id"}],Imm}, Gen = fun(_Fd, N) -> - enc_objset(Erule, Name, N, ObjSet, - RestFieldNames, Extensible) + Aligned = is_aligned(Erule), + emit([{asis,N},"(Val, Id) ->",nl]), + asn1ct_imm:enc_cg(Imm, Aligned), + emit([".",nl]) end, Prefix = lists:concat(["enc_os_",Name]), - F = asn1ct_func:call_gen(Prefix, Key, Gen), - [{apply,F,[{var,atom_to_list(Val)},{var,Fun}]}]. + [{call_gen,Prefix,Key,Gen,Lambda,[Val,Fun]}]. fix_object_code(Name, [{Name,B}|_], _ClassFields) -> B; @@ -1042,9 +1059,7 @@ fix_object_code(Name, [], ClassFields) -> end end. - -enc_objset(Erule, Component, Name, ObjSet, RestFieldNames, Extensible) -> - asn1ct_name:start(), +enc_objset_imm(Erule, Component, ObjSet, RestFieldNames, Extensible) -> Aligned = is_aligned(Erule), E = {error, fun() -> @@ -1053,22 +1068,28 @@ enc_objset(Erule, Component, Name, ObjSet, RestFieldNames, Extensible) -> "{value,Val}," "{unique_name_and_value,'_'}})",nl]) end}, - Imm = [{'cond', - [[{eq,{var,"Id"},Key}| - enc_obj(Erule, Obj, RestFieldNames, Aligned)] || - {Key,Obj} <- ObjSet] ++ - [['_',case Extensible of - false -> E; - true -> {put_bits,{var,"Val"},binary,[1]} - end]]}], - emit([{asis,Name},"(Val, Id) ->",nl]), - asn1ct_imm:enc_cg(Imm, Aligned), - emit([".",nl]). + [{'cond', + [[{eq,{var,"Id"},Key}| + enc_obj(Erule, Obj, RestFieldNames, Aligned)] || + {Key,Obj} <- ObjSet] ++ + [['_',case Extensible of + false -> + E; + true -> + case asn1ct:use_legacy_types() of + false -> + {call,per_common,open_type_to_binary, + [{var,"Val"}]}; + true -> + {call,per_common,legacy_open_type_to_binary, + [{var,"Val"}]} + end + end]]}]. enc_obj(Erule, Obj, RestFieldNames0, Aligned) -> case Obj of #typedef{name={primitive,bif},typespec=Def} -> - asn1ct_gen_per:gen_encode_prim_imm('Val', Def, Aligned); + asn1ct_gen_per:gen_encode_prim_imm({var,"Val"}, Def, Aligned); #typedef{name={constructed,bif},typespec=Def} -> InnerType = asn1ct_gen:get_inner(Def#type.def), case InnerType of @@ -1084,7 +1105,7 @@ enc_obj(Erule, Obj, RestFieldNames0, Aligned) -> gen_encode_sof_imm(Erule, name, InnerType, Def) end; #typedef{name=Type} -> - [{apply,enc_func(Type),[{var,"Val"}]}]; + [{apply,{local,enc_func(Type),Type},[{var,"Val"}]}]; #'Externalvaluereference'{module=Mod,value=Value} -> case asn1_db:dbget(Mod, Value) of #typedef{typespec=#'Object'{def=Def}} -> @@ -1097,9 +1118,9 @@ enc_obj(Erule, Obj, RestFieldNames0, Aligned) -> Func = enc_func(Type), case get(currmod) of Mod -> - [{apply,Func,[{var,"Val"}]}]; + [{apply,{local,Func,Obj},[{var,"Val"}]}]; _ -> - [{apply,{Mod,Func},[{var,"Val"}]}] + [{apply,{Mod,Func,Obj},[{var,"Val"}]}] end end. @@ -1540,12 +1561,12 @@ gen_enc_choices([H|T], Erule, TopType, Pos, Constr, Ext) -> no -> case Type#type.tablecinf of [{objfun,_}|_] -> - {"got objfun through args","ObjFun"}; + {"got objfun through args",{var,"ObjFun"}}; _ -> false end; _ -> - {no_attr,"ObjFun"} + {no_attr,{var,"ObjFun"}} end, DoExt = case Constr of ext -> Ext; @@ -1561,7 +1582,7 @@ gen_enc_choices([H|T], Erule, TopType, Pos, Constr, Ext) -> [{put_bits,0,1,[1]}| asn1ct_imm:per_enc_integer(Pos, Constr, Aligned)] end, - Body = gen_enc_line_imm(Erule, TopType, Cname, Type, 'ChoiceVal', + Body = gen_enc_line_imm(Erule, TopType, Cname, Type, {var,"ChoiceVal"}, EncObj, DoExt), Imm = Tag ++ Body, [{Cname,Imm}|gen_enc_choices(T, Erule, TopType, Pos+1, Constr, Ext)]; @@ -1778,3 +1799,13 @@ value_match1(Value,[],Acc,Depth) -> Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")")); value_match1(Value,[{VI,_}|VIs],Acc,Depth) -> value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1). + +enc_dig_out_value([], Value) -> + {[],Value}; +enc_dig_out_value([{N,_}|T], Value) -> + {Imm0,Dst0} = enc_dig_out_value(T, Value), + {Imm,Dst} = asn1ct_imm:enc_element(N, Dst0), + {Imm0++Imm,Dst}. + +make_var(Base) -> + {var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(Base)))}. diff --git a/lib/asn1/src/asn1ct_func.erl b/lib/asn1/src/asn1ct_func.erl index dbadedb683..fb94f65b32 100644 --- a/lib/asn1/src/asn1ct_func.erl +++ b/lib/asn1/src/asn1ct_func.erl @@ -19,7 +19,8 @@ %% -module(asn1ct_func). --export([start_link/0,need/1,call/3,call_gen/3,call_gen/4,generate/1]). +-export([start_link/0,need/1,call/3,call_gen/3,call_gen/4, + generate/1,is_used/1]). -export([init/1,handle_call/3,handle_cast/2,terminate/2]). start_link() -> @@ -48,7 +49,7 @@ need(MFA) -> call_gen(Prefix, Key, Gen, Args) when is_function(Gen, 2) -> F = req({gen_func,Prefix,Key,Gen}), - asn1ct_gen:emit([F,"(",call_args(Args, ""),")"]). + asn1ct_gen:emit([{asis,F},"(",call_args(Args, ""),")"]). call_gen(Prefix, Key, Gen) when is_function(Gen, 2) -> req({gen_func,Prefix,Key,Gen}). @@ -63,6 +64,10 @@ generate(Fd) -> Funcs = sofs:to_external(Funcs0), ok = file:write(Fd, Funcs). +is_used({_,_,_}=MFA) -> + req({is_used,MFA}). + + req(Req) -> gen_server:call(get(?MODULE), Req, infinity). @@ -103,7 +108,10 @@ handle_call({gen_func,Prefix,Key,GenFun}, _From, #st{gen=G0,gc=Gc0}=St) -> {reply,Name,St#st{gen=G,gc=Gc}}; {value,{Name,_}} -> {reply,Name,St} - end. + end; +handle_call({is_used,MFA}, _From, #st{used=Used}=St) -> + {reply,gb_sets:is_member(MFA, Used),St}. + terminate(_, _) -> ok. diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index 30d337635b..450d309688 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -23,12 +23,12 @@ -export([demit/1, emit/1, + open_output_file/1,close_output_file/0, get_inner/1,type/1,def_to_tag/1,prim_bif/1, list2name/1, list2rname/1, constructed_suffix/2, unify_if_string/1, - gen_check_call/7, get_constraint/2, insert_once/2, ct_gen_module/1, @@ -42,6 +42,8 @@ -export([gen_encode_constructed/4, gen_decode_constructed/4]). +-define(SUPPRESSION_FUNC, 'dialyzer-suppressions'). + %% pgen(Outfile, Erules, Module, TypeOrVal, Options) %% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module %% .hrl file is only generated if necessary @@ -70,8 +72,7 @@ pgen_module(OutFile,Erules,Module, HrlGenerated = pgen_hrl(Erules,Module,TypeOrVal,Options,Indent), asn1ct_name:start(), ErlFile = lists:concat([OutFile,".erl"]), - Fid = fopen(ErlFile), - put(gen_file_out,Fid), + _ = open_output_file(ErlFile), asn1ct_func:start_link(), gen_head(Erules,Module,HrlGenerated), pgen_exports(Erules,Module,TypeOrVal), @@ -85,12 +86,18 @@ pgen_module(OutFile,Erules,Module, "%%%",nl, "%%% Run-time functions.",nl, "%%%",nl]), - asn1ct_func:generate(Fid), - file:close(Fid), - _ = erase(gen_file_out), + dialyzer_suppressions(Erules), + Fd = get(gen_file_out), + asn1ct_func:generate(Fd), + close_output_file(), _ = erase(outfile), asn1ct:verbose("--~p--~n",[{generated,ErlFile}],Options). +dialyzer_suppressions(Erules) -> + emit([nl, + {asis,?SUPPRESSION_FUNC},"(Arg) ->",nl]), + Rtmod = ct_gen_module(Erules), + Rtmod:dialyzer_suppressions(Erules). pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) -> Rtmod = ct_gen_module(Erules), @@ -98,11 +105,6 @@ pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects pgen_values(Erules,Module,Values), pgen_objects(Rtmod,Erules,Module,Objects), pgen_objectsets(Rtmod,Erules,Module,ObjectSets), - case catch lists:member(der,get(encoding_options)) of - true -> - pgen_check_defaultval(Erules,Module); - _ -> ok - end, pgen_partial_decode(Rtmod,Erules,Module). pgen_values(_,_,[]) -> @@ -178,23 +180,6 @@ pgen_objectsets(Rtmod,Erules,Module,[H|T]) -> Rtmod:gen_objectset_code(Erules,TypeDef), pgen_objectsets(Rtmod,Erules,Module,T). -pgen_check_defaultval(Erules,Module) -> - CheckObjects = asn1ct_table:to_list(check_functions), - case get(asndebug) of - true -> - FileName = lists:concat([Module,".table"]), - {ok,IoDevice} = file:open(FileName,[write]), - Fun = - fun(X)-> - io:format(IoDevice,"~n~n************~n~n~p~n~n*****" - "********~n~n",[X]) - end, - lists:foreach(Fun,CheckObjects), - file:close(IoDevice); - _ -> ok - end, - gen_check_defaultval(Erules,Module,CheckObjects). - pgen_partial_decode(Rtmod,Erule,Module) when Erule == ber -> pgen_partial_inc_dec(Rtmod,Erule,Module), pgen_partial_dec(Rtmod,Erule,Module); @@ -542,8 +527,7 @@ gen_part_decode_funcs({constructed,bif},TypeName, emit([" 'dec_",TypeName,"'(Data,",{asis,Tag},")"]); gen_part_decode_funcs({primitive,bif},_TypeName, {_Name,undecoded,Tag,Type}) -> - % Argument no 6 is 0, i.e. bit 6 for primitive encoding. - asn1ct_gen_ber_bin_v2:gen_dec_prim(ber_bin_v2,Type,"Data",Tag,[],0,", mandatory, "); + asn1ct_gen_ber_bin_v2:gen_dec_prim(Type, "Data", Tag); gen_part_decode_funcs(WhatKind,_TypeName,{_,Directive,_,_}) -> throw({error,{asn1,{"Not implemented yet",WhatKind," partial incomplete directive:",Directive}}}). @@ -576,131 +560,6 @@ gen_types(Erules,Tname,Type) when is_record(Type,type) -> asn1ct_name:clear(), Rtmod:gen_decode(Erules,Tname,Type). -gen_check_defaultval(Erules,Module,[{Name,Type}|Rest]) -> - gen_check_func(Name,Type), - gen_check_defaultval(Erules,Module,Rest); -gen_check_defaultval(_,_,[]) -> - ok. - -gen_check_func(Name,FType = #type{def=Def}) -> - EncName = ensure_atom(Name), - emit({{asis,EncName},"(_V,asn1_DEFAULT) ->",nl," true;",nl}), - emit({{asis,EncName},"(V,V) ->",nl," true;",nl}), - emit({{asis,EncName},"(V,{_,V}) ->",nl," true;",nl}), - case Def of - {'SEQUENCE OF',Type} -> - gen_check_sof(Name,'SEQOF',Type); - {'SET OF',Type} -> - gen_check_sof(Name,'SETOF',Type); - #'SEQUENCE'{components=Components} -> - gen_check_sequence(Name,Components); - #'SET'{components=Components} -> - gen_check_sequence(Name,Components); - {'CHOICE',Components} -> - gen_check_choice(Name,Components); - #'Externaltypereference'{type=T} -> - emit({{asis,EncName},"(DefaultValue,Value) ->",nl}), - emit({" '",list2name([T,check]),"'(DefaultValue,Value).",nl}); - MaybePrim -> - InnerType = get_inner(MaybePrim), - case type(InnerType) of - {primitive,bif} -> - emit({{asis,EncName},"(DefaultValue,Value) ->",nl," "}), - gen_prim_check_call(get_inner(InnerType),"DefaultValue","Value", - FType), - emit({".",nl,nl}); - _ -> - throw({asn1_error,{unknown,type,MaybePrim}}) - end - end. - -gen_check_sof(Name,SOF,Type) -> - EncName = ensure_atom(Name), - NewName = ensure_atom(list2name([sorted,Name])), - emit({{asis,EncName},"(V1,V2) ->",nl}), - emit({" ",{asis,NewName},"(lists:sort(V1),lists:sort(V2)).",nl,nl}), - emit({{asis,NewName},"([],[]) ->",nl," true;",nl}), - emit({{asis,NewName},"([DV|DVs],[V|Vs]) ->",nl," "}), - InnerType = get_inner(Type#type.def), - case type(InnerType) of - {primitive,bif} -> - gen_prim_check_call(get_inner(InnerType),"DV","V",Type), - emit({",",nl}); - {constructed,bif} -> - emit([{asis,ensure_atom(list2name([SOF,Name]))},"(DV, V),",nl]); - #'Externaltypereference'{type=T} -> - emit([{asis,ensure_atom(list2name([T,check]))},"(DV,V),",nl]); - 'ASN1_OPEN_TYPE' -> - emit(["DV = V,",nl]); - _ -> - emit(["DV = V,",nl]) - end, - emit({" ",{asis,NewName},"(DVs,Vs).",nl,nl}). - -gen_check_sequence(Name, []) -> - emit([{asis,ensure_atom(Name)},"(_,_) ->",nl, - " throw(badval).",nl,nl]); -gen_check_sequence(Name,Components) -> - emit([{asis,ensure_atom(Name)},"(DefaultValue,Value) ->",nl]), - gen_check_sequence(Name,Components,1). - -gen_check_sequence(Name,[#'ComponentType'{name=N,typespec=Type}|Cs],Num) -> - InnerType = get_inner(Type#type.def), - NthDefV = ["element(",Num+1,",DefaultValue)"], - NthV = ["element(",Num+1,",Value)"], - gen_check_func_call(Name,Type,InnerType,NthDefV,NthV,N), - case Cs of - [] -> - emit({".",nl,nl}); - _ -> - emit({",",nl}), - gen_check_sequence(Name,Cs,Num+1) - end. - -gen_check_choice(Name,CList=[#'ComponentType'{}|_Cs]) -> - emit([{asis,ensure_atom(Name)},"({Id,DefaultValue},{Id,Value}) ->",nl]), - emit([" case Id of",nl]), - gen_check_choice_components(Name,CList,1). - -gen_check_choice_components(_,[],_)-> - ok; -gen_check_choice_components(Name,[#'ComponentType'{name=N,typespec=Type}| - Cs],Num) -> - Ind6 = " ", - InnerType = get_inner(Type#type.def), - emit({Ind6,"'",N,"' ->",nl,Ind6}), - gen_check_func_call(Name,Type,InnerType,{var,"defaultValue"}, - {var,"value"},N), - case Cs of - [] -> - emit({nl," end.",nl,nl}); - _ -> - emit({";",nl}), - gen_check_choice_components(Name,Cs,Num+1) - end. - -gen_check_func_call(Name,Type,InnerType,DefVal,Val,N) -> - case type(InnerType) of - {primitive,bif} -> - emit(" "), - gen_prim_check_call(get_inner(InnerType),DefVal,Val,Type); - #'Externaltypereference'{type=T} -> - emit({" ",{asis,ensure_atom(list2name([T,check]))},"(",DefVal,",",Val,")"}); - 'ASN1_OPEN_TYPE' -> - emit([" if",nl, - " ",DefVal," == ",Val," -> true;",nl, - " true -> throw({error,{asn1_open_type}})",nl, - " end",nl]); - {constructed,bif} -> - emit([" ",{asis,ensure_atom(list2name([N,Name]))},"(",DefVal,",",Val,")"]); - _ -> - emit([" if",nl, - " ",DefVal," == ",Val," -> true;",nl, - " true -> throw({error,{asn1_open_type}})",nl, - " end",nl]) - end. - - %% VARIOUS GENERATOR STUFF %% ************************************************* %%************************************************** @@ -790,7 +649,9 @@ gen_decode_constructed(Erules,Typename,InnerType,D) when is_record(D,typedef) -> pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) -> - emit(["-export([encoding_rule/0,bit_string_format/0]).",nl]), + emit(["-export([encoding_rule/0,bit_string_format/0,",nl, + " legacy_erlang_types/0]).",nl]), + emit(["-export([",{asis,?SUPPRESSION_FUNC},"/1]).",nl]), case Types of [] -> ok; _ -> @@ -1022,7 +883,9 @@ gen_info_functions(Erules) -> emit(["encoding_rule() -> ", {asis,Erules},".",nl,nl, "bit_string_format() -> ", - {asis,asn1ct:get_bit_string_format()},".",nl,nl]). + {asis,asn1ct:get_bit_string_format()},".",nl,nl, + "legacy_erlang_types() -> ", + {asis,asn1ct:use_legacy_types()},".",nl,nl]). gen_decode_partial_incomplete(ber) -> case {asn1ct:read_config_data(partial_incomplete_decode), @@ -1074,9 +937,10 @@ gen_partial_inc_dispatcher() -> ok; {Data1,Data2} -> % io:format("partial_incomplete_decode: ~p~ninc_type_pattern: ~p~n",[Data,Data2]), - gen_partial_inc_dispatcher(Data1,Data2) + gen_partial_inc_dispatcher(Data1, Data2, "") end. -gen_partial_inc_dispatcher([{FuncName,TopType,_Pattern}|Rest],TypePattern) -> + +gen_partial_inc_dispatcher([{FuncName,TopType,_Pattern}|Rest], TypePattern, Sep) -> TPattern = case lists:keysearch(FuncName,1,TypePattern) of {value,{_,TP}} -> TP; @@ -1090,13 +954,13 @@ gen_partial_inc_dispatcher([{FuncName,TopType,_Pattern}|Rest],TypePattern) -> _ -> atom_to_list(TopType) end, - emit(["decode_partial_inc_disp('",TopTypeName,"',Data) ->",nl, + emit([Sep, + "decode_partial_inc_disp('",TopTypeName,"',Data) ->",nl, " ",{asis,list_to_atom(lists:concat(["dec-inc-",FuncName2]))}, - "(Data);",nl]), - gen_partial_inc_dispatcher(Rest,TypePattern); -gen_partial_inc_dispatcher([],_) -> - emit(["decode_partial_inc_disp(Type,_Data) ->",nl, - " exit({error,{asn1,{undefined_type,Type}}}).",nl]). + "(Data)"]), + gen_partial_inc_dispatcher(Rest, TypePattern, ";\n"); +gen_partial_inc_dispatcher([], _, _) -> + emit([".",nl]). gen_dispatcher([F1,F2|T],FuncName,Prefix,ExtraArg) -> emit([FuncName,"('",F1,"',Data) -> '",Prefix,F1,"'(Data",ExtraArg,")",";",nl]), @@ -1121,9 +985,23 @@ pgen_info() -> open_hrl(OutFile,Module) -> File = lists:concat([OutFile,".hrl"]), - Fid = fopen(File), - put(gen_file_out,Fid), - gen_hrlhead(Module). + _ = open_output_file(File), + gen_hrlhead(Module), + Protector = hrl_protector(OutFile), + emit(["-ifndef(",Protector,").\n", + "-define(",Protector,", true).\n" + "\n"]). + +hrl_protector(OutFile) -> + BaseName = filename:basename(OutFile), + P = "_" ++ string:to_upper(BaseName) ++ "_HRL_", + [if + $A =< C, C =< $Z -> C; + $a =< C, C =< $a -> C; + $0 =< C, C =< $9 -> C; + true -> $_ + end || C <- P]. + %% EMIT functions ************************ %% *************************************** @@ -1195,15 +1073,19 @@ call_args([A|As], Sep) -> [Sep,do_emit(A)|call_args(As, ", ")]; call_args([], _) -> []. -fopen(F) -> +open_output_file(F) -> case file:open(F, [write,raw,delayed_write]) of - {ok, Fd} -> + {ok,Fd} -> + put(gen_file_out, Fd), Fd; {error, Reason} -> io:format("** Can't open file ~p ~n", [F]), exit({error,Reason}) end. +close_output_file() -> + ok = file:close(erase(gen_file_out)). + pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) -> put(currmod,Module), {Types,Values,Ptypes,_,_,_} = TypeOrVal, @@ -1226,8 +1108,9 @@ pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) -> 0 -> 0; Y -> - Fid = get(gen_file_out), - file:close(Fid), + Protector = hrl_protector(get(outfile)), + emit(["-endif. %% ",Protector,"\n"]), + close_output_file(), asn1ct:verbose("--~p--~n", [{generated,lists:concat([get(outfile),".hrl"])}], Options), @@ -1443,171 +1326,6 @@ to_textual_order(Cs=[#'ComponentType'{textual_order=undefined}|_]) -> to_textual_order(Cs) when is_list(Cs) -> lists:keysort(#'ComponentType'.textual_order,Cs). - -gen_check_call(TopType,Cname,Type,InnerType,WhatKind,DefaultValue,Element) -> - case WhatKind of - {primitive,bif} -> - gen_prim_check_call(InnerType,DefaultValue,Element,Type); - #'Externaltypereference'{module=M,type=T} -> - %% generate function call - Name = list2name([T,check]), - emit({"'",Name,"'(",DefaultValue,", ",Element,")"}), - %% insert in ets table and do look ahead check - Typedef = asn1_db:dbget(M,T), - RefType = Typedef#typedef.typespec, - InType = asn1ct_gen:get_inner(RefType#type.def), - case insert_once(check_functions,{Name,RefType}) of - true -> - lookahead_innertype([T],InType,RefType); - _ -> - ok - end; - {constructed,bif} -> - NameList = [Cname|TopType], - Name = list2name(NameList ++ [check]), - emit({"'",Name,"'(",DefaultValue,", ",Element,")"}), - asn1ct_table:insert(check_functions, {Name, Type}), - %% Must look for check functions in InnerType, - %% that may be referenced or internal defined - %% constructed types not used elsewhere. - lookahead_innertype(NameList,InnerType,Type); - _ -> - %% Generate Dummy function call i.e. anything is accepted - emit(["fun() -> true end ()"]) - end. - -gen_prim_check_call(PrimType, Default, Element, Type) -> - case unify_if_string(PrimType) of - 'BOOLEAN' -> - check_call(check_bool, [Default,Element]); - 'INTEGER' -> - NNL = case Type#type.def of - {_,NamedNumberList} -> NamedNumberList; - _ -> [] - end, - check_call(check_int, [Default,Element,{asis,NNL}]); - 'BIT STRING' -> - case Type#type.def of - {_,[]} -> - check_call(check_bitstring, - [Default,Element]); - {_,[_|_]=NBL} -> - check_call(check_named_bitstring, - [Default,Element,{asis,NBL}]) - end; - 'OCTET STRING' -> - check_call(check_octetstring, [Default,Element]); - 'NULL' -> - check_call(check_null, [Default,Element]); - 'OBJECT IDENTIFIER' -> - check_call(check_objectidentifier, [Default,Element]); - 'RELATIVE-OID' -> - check_call(check_objectidentifier, [Default,Element]); - 'ObjectDescriptor' -> - check_call(check_objectdescriptor, [Default,Element]); - 'REAL' -> - check_call(check_real, [Default,Element]); - 'ENUMERATED' -> - {_,Enumerations} = Type#type.def, - check_call(check_enum, [Default,Element,{asis,Enumerations}]); - restrictedstring -> - check_call(check_restrictedstring, [Default,Element]) - end. - -check_call(F, Args) -> - asn1ct_func:call(check, F, Args). - -%% lokahead_innertype/3 traverses Type and checks if check functions -%% have to be generated, i.e. for all constructed or referenced types. -lookahead_innertype(Name,'SEQUENCE',Type) -> - Components = (Type#type.def)#'SEQUENCE'.components, - lookahead_components(Name,Components); -lookahead_innertype(Name,'SET',Type) -> - Components = (Type#type.def)#'SET'.components, - lookahead_components(Name,Components); -lookahead_innertype(Name,'CHOICE',Type) -> - {_,Components} = Type#type.def, - lookahead_components(Name,Components); -lookahead_innertype(Name,'SEQUENCE OF',SeqOf) -> - lookahead_sof(Name,'SEQOF',SeqOf); -lookahead_innertype(Name,'SET OF',SeqOf) -> - lookahead_sof(Name,'SETOF',SeqOf); -lookahead_innertype(_Name,#'Externaltypereference'{module=M,type=T},_) -> - Typedef = asn1_db:dbget(M,T), - RefType = Typedef#typedef.typespec, - insert_once(check_functions,{list2name([T,check]),RefType}), - InType = asn1ct_gen:get_inner(RefType#type.def), - case type(InType) of - {constructed,bif} -> - lookahead_innertype([T],InType,RefType); - Ref = #'Externaltypereference'{} -> - lookahead_reference(Ref); - _ -> - ok - end; -lookahead_innertype(_,_,_) -> - ok. - -lookahead_components(_,[]) -> ok; -lookahead_components(Name,[C|Cs]) -> - #'ComponentType'{name=Cname,typespec=Type} = C, - InType = asn1ct_gen:get_inner(Type#type.def), - case asn1ct_gen:type(InType) of - {constructed,bif} -> - case insert_once(check_functions, - {list2name([Cname|Name] ++ [check]),Type}) of - true -> - lookahead_innertype([Cname|Name],InType,Type); - _ -> - ok - end; - #'Externaltypereference'{module=RefMod,type=RefName} -> - Typedef = asn1_db:dbget(RefMod,RefName), - RefType = Typedef#typedef.typespec, - case insert_once(check_functions,{list2name([RefName,check]), - RefType}) of - true -> - lookahead_innertype([RefName],InType,RefType); - _ -> - ok - end; - _ -> - ok - end, - lookahead_components(Name,Cs). - -lookahead_sof(Name,SOF,SOFType) -> - Type = case SOFType#type.def of - {_,_Type} -> _Type; - _Type -> _Type - end, - InnerType = asn1ct_gen:get_inner(Type#type.def), - case asn1ct_gen:type(InnerType) of - {constructed,bif} -> - %% this is if a constructed type is defined in - %% the SEQUENCE OF type - NameList = [SOF|Name], - insert_once(check_functions, - {list2name(NameList ++ [check]),Type}), - lookahead_innertype(NameList,InnerType,Type); - Ref = #'Externaltypereference'{} -> - lookahead_reference(Ref); - _ -> - ok - end. - -lookahead_reference(#'Externaltypereference'{module=M,type=T}) -> - Typedef = asn1_db:dbget(M,T), - RefType = Typedef#typedef.typespec, - InType = get_inner(RefType#type.def), - case insert_once(check_functions, - {list2name([T,check]),RefType}) of - true -> - lookahead_innertype([T],InType,RefType); - _ -> - ok - end. - insert_once(Table,Object) -> case asn1ct_table:lookup(Table, element(1, Object)) of [] -> @@ -1661,6 +1379,11 @@ conform_value(#type{def={'BIT STRING',[]}}, Bs) -> bitstring when is_bitstring(Bs) -> Bs end; +conform_value(#type{def='OCTET STRING'}, String) -> + case asn1ct:use_legacy_types() of + false -> String; + true -> binary_to_list(String) + end; conform_value(_, Value) -> Value. named_bitstring_value(List, Names) -> @@ -1879,11 +1602,6 @@ get_constraint(C,Key) -> {value,Cnstr} -> Cnstr end. - -ensure_atom(Atom) when is_atom(Atom) -> - Atom; -ensure_atom(List) when is_list(List) -> - list_to_atom(List). get_record_name_prefix() -> case lists:keysearch(record_name_prefix,1,get(encoding_options)) of diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index de81259fcb..e51b0898be 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -27,11 +27,12 @@ -export([decode_class/1, decode_type/1]). -export([gen_encode/2,gen_encode/3,gen_decode/2,gen_decode/3]). -export([gen_encode_prim/4]). --export([gen_dec_prim/7]). +-export([gen_dec_prim/3]). -export([gen_objectset_code/2, gen_obj_code/3]). -export([encode_tag_val/3]). -export([gen_inc_decode/2,gen_decode_selected/3]). -export([extaddgroup2sequence/1]). +-export([dialyzer_suppressions/1]). -import(asn1ct_gen, [emit/1,demit/1]). @@ -65,6 +66,23 @@ %%=============================================================================== %%=============================================================================== +dialyzer_suppressions(_) -> + case asn1ct:use_legacy_types() of + false -> ok; + true -> suppress({ber,encode_bit_string,4}) + end, + suppress({ber,decode_selective,2}), + emit([" ok.",nl]). + +suppress({M,F,A}=MFA) -> + case asn1ct_func:is_used(MFA) of + false -> + ok; + true -> + Args = [lists:concat(["element(",I,", Arg)"]) || I <- lists:seq(1, A)], + emit([" ",{call,M,F,Args},com,nl]) + end. + %%=============================================================================== %% encode #{typedef, {pos, name, typespec}} %%=============================================================================== @@ -163,6 +181,12 @@ gen_encode_user(Erules, #typedef{}=D, Wrapper) -> gen_encode_prim(_Erules, #type{}=D, DoTag, Value) -> BitStringConstraint = get_size_constraint(D#type.constraint), + MaxBitStrSize = case BitStringConstraint of + [] -> none; + {_,'MAX'} -> none; + {_,Max} -> Max; + Max when is_integer(Max) -> Max + end, asn1ct_name:new(enumval), Type = case D#type.def of 'OCTET STRING' -> restricted_string; @@ -206,10 +230,32 @@ gen_encode_prim(_Erules, #type{}=D, DoTag, Value) -> {call,ber,encode_tags, [DoTag,{curr,realval},{curr,realsize}]},nl, "end"]); + {'BIT STRING',[]} -> + case asn1ct:use_legacy_types() of + false when MaxBitStrSize =:= none -> + call(encode_unnamed_bit_string, [Value,DoTag]); + false -> + call(encode_unnamed_bit_string, + [{asis,MaxBitStrSize},Value,DoTag]); + true -> + call(encode_bit_string, + [{asis,BitStringConstraint},Value, + {asis,[]},DoTag]) + end; {'BIT STRING',NamedNumberList} -> - call(encode_bit_string, - [{asis,BitStringConstraint},Value, - {asis,NamedNumberList},DoTag]); + case asn1ct:use_legacy_types() of + false when MaxBitStrSize =:= none -> + call(encode_named_bit_string, + [Value,{asis,NamedNumberList},DoTag]); + false -> + call(encode_named_bit_string, + [{asis,MaxBitStrSize},Value, + {asis,NamedNumberList},DoTag]); + true -> + call(encode_bit_string, + [{asis,BitStringConstraint},Value, + {asis,NamedNumberList},DoTag]) + end; 'NULL' -> call(encode_null, [Value,DoTag]); 'OBJECT IDENTIFIER' -> @@ -232,14 +278,20 @@ emit_enc_enumerated_cases(L, Tags) -> emit_enc_enumerated_cases(L, Tags, noext). emit_enc_enumerated_cases([{EnumName,EnumVal}|T], Tags, Ext) -> + Bytes = encode_pos_integer(EnumVal, []), + Len = length(Bytes), emit([{asis,EnumName}," -> ", - {call,ber,encode_enumerated,[EnumVal,Tags]},";",nl]), + {call,ber,encode_tags,[Tags,{asis,Bytes},Len]},";",nl]), emit_enc_enumerated_cases(T, Tags, Ext); emit_enc_enumerated_cases([], _Tags, _Ext) -> %% FIXME: Should extension be handled? emit([{curr,enumval}," -> exit({error,{asn1, {enumerated_not_in_range,",{curr, enumval},"}}})"]), emit([nl,"end"]). +encode_pos_integer(0, [B|_Acc] = L) when B < 128 -> + L; +encode_pos_integer(N, Acc) -> + encode_pos_integer(N bsr 8, [N band 255|Acc]). %%=============================================================================== %%=============================================================================== @@ -317,15 +369,11 @@ gen_decode_selected_type(_Erules,TypeDef) -> case asn1ct_gen:type(InnerType) of 'ASN1_OPEN_TYPE' -> asn1ct_name:new(len), - gen_dec_prim(ber, Def#type{def='ASN1_OPEN_TYPE'}, - BytesVar,Tag, [] , - ?PRIMITIVE,"OptOrMand"); -% emit({";",nl}); + gen_dec_prim(Def#type{def='ASN1_OPEN_TYPE'}, + BytesVar, Tag); {primitive,bif} -> asn1ct_name:new(len), - gen_dec_prim(ber, Def, BytesVar,Tag,[] , - ?PRIMITIVE,"OptOrMand"); -% emit([";",nl]); + gen_dec_prim(Def, BytesVar, Tag); {constructed,bif} -> TopType = case TypeDef#typedef.name of A when is_atom(A) -> [A]; @@ -439,14 +487,12 @@ gen_decode_user(Erules,D) when is_record(D,typedef) -> case asn1ct_gen:type(InnerType) of 'ASN1_OPEN_TYPE' -> asn1ct_name:new(len), - gen_dec_prim(ber, Def#type{def='ASN1_OPEN_TYPE'}, - BytesVar,{string,"TagIn"}, [] , - ?PRIMITIVE,"OptOrMand"), + gen_dec_prim(Def#type{def='ASN1_OPEN_TYPE'}, + BytesVar, {string,"TagIn"}), emit({".",nl,nl}); {primitive,bif} -> asn1ct_name:new(len), - gen_dec_prim(ber, Def, BytesVar,{string,"TagIn"},[] , - ?PRIMITIVE,"OptOrMand"), + gen_dec_prim(Def, BytesVar, {string,"TagIn"}), emit([".",nl,nl]); {constructed,bif} -> asn1ct:update_namelist(D#typedef.name), @@ -459,19 +505,11 @@ gen_decode_user(Erules,D) when is_record(D,typedef) -> end. -gen_dec_prim(_Erules, Att, BytesVar, DoTag, _TagIn, _Form, _OptOrMand) -> +gen_dec_prim(Att, BytesVar, DoTag) -> Typename = Att#type.def, -%% Currently not used for BER replaced with [] as place holder -%% Constraint = Att#type.constraint, -%% Constraint = [], Constraint = get_size_constraint(Att#type.constraint), IntConstr = int_constr(Att#type.constraint), - AsBin = case get(binary_strings) of - true -> "_as_bin"; - _ -> "" - end, NewTypeName = case Typename of - 'OCTET STRING' -> restricted_string; 'NumericString' -> restricted_string; 'TeletexString' -> restricted_string; 'T61String' -> restricted_string; @@ -484,85 +522,40 @@ gen_dec_prim(_Erules, Att, BytesVar, DoTag, _TagIn, _Form, _OptOrMand) -> 'ObjectDescriptor'-> restricted_string; 'UTCTime' -> restricted_string; 'GeneralizedTime' -> restricted_string; + 'OCTET STRING' -> + case asn1ct:use_legacy_types() of + true -> restricted_string; + false -> Typename + end; _ -> Typename end, - case NewTypeName of - 'BOOLEAN'-> - emit(["decode_boolean(",BytesVar,","]), - need(decode_boolean, 2); - 'INTEGER' -> - case IntConstr of - [] -> - emit(["decode_integer(",BytesVar,","]), - need(decode_integer, 2); - {_,_} -> - emit(["decode_integer(",BytesVar,",", - {asis,IntConstr},","]), - need(decode_integer, 3) - end; - {'INTEGER',NamedNumberList} -> - case IntConstr of - [] -> - emit(["decode_named_integer(",BytesVar,",", - {asis,NamedNumberList},","]), - need(decode_named_integer, 3); - {_,_} -> - emit(["decode_named_integer(",BytesVar,",", - {asis,IntConstr},",", - {asis,NamedNumberList},","]), - need(decode_named_integer, 4) - end; - {'ENUMERATED',NamedNumberList} -> - emit(["decode_enumerated(",BytesVar,",", - {asis,NamedNumberList},","]), - need(decode_enumerated, 3); - 'REAL' -> - ok; - {'BIT STRING',_NamedNumberList} -> - ok; - 'NULL' -> - emit(["decode_null(",BytesVar,","]), - need(decode_null, 2); - 'OBJECT IDENTIFIER' -> - emit(["decode_object_identifier(",BytesVar,","]), - need(decode_object_identifier, 2); - 'RELATIVE-OID' -> - emit(["decode_relative_oid(",BytesVar,","]), - need(decode_relative_oid, 2); - restricted_string -> - emit(["decode_restricted_string",AsBin,"(",BytesVar,","]), - case Constraint of - [] -> - need(decode_restricted_string, 2); - _ -> - emit([{asis,Constraint},","]), - need(decode_restricted_string, 3) - end; - 'UniversalString' -> - emit(["decode_universal_string",AsBin,"(", - BytesVar,",",{asis,Constraint},","]), - need(decode_universal_string, 3); - 'UTF8String' -> - emit(["decode_UTF8_string",AsBin,"(", - BytesVar,","]), - need(decode_UTF8_string, 2); - 'BMPString' -> - emit(["decode_BMP_string",AsBin,"(", - BytesVar,",",{asis,Constraint},","]), - need(decode_BMP_string, 3); - 'ASN1_OPEN_TYPE' -> - emit(["decode_open_type_as_binary(", - BytesVar,","]), - need(decode_open_type_as_binary, 2) - end, - TagStr = case DoTag of {string,Tag1} -> Tag1; _ when is_list(DoTag) -> {asis,DoTag} end, case NewTypeName of - {'BIT STRING',NNL} -> - gen_dec_bit_string(BytesVar, Constraint, NNL, TagStr); + 'BOOLEAN'-> + call(decode_boolean, [BytesVar,TagStr]); + 'INTEGER' -> + check_constraint(decode_integer, [BytesVar,TagStr], + IntConstr, + identity, + identity); + {'INTEGER',NNL} -> + check_constraint(decode_integer, + [BytesVar,TagStr], + IntConstr, + identity, + fun(Val) -> + asn1ct_name:new(val), + emit([{curr,val}," = "]), + Val(), + emit([com,nl, + {call,ber,number2name, + [{curr,val},{asis,NNL}]}]) + end); + {'ENUMERATED',NNL} -> + gen_dec_enumerated(BytesVar, NNL, TagStr); 'REAL' -> asn1ct_name:new(tmpbuf), emit(["begin",nl, @@ -570,8 +563,36 @@ gen_dec_prim(_Erules, Att, BytesVar, DoTag, _TagIn, _Form, _OptOrMand) -> {call,ber,match_tags,[BytesVar,TagStr]},com,nl, {call,real_common,decode_real,[{curr,tmpbuf}]},nl, "end",nl]); - _ -> - emit([TagStr,")"]) + {'BIT STRING',NNL} -> + gen_dec_bit_string(BytesVar, Constraint, NNL, TagStr); + 'NULL' -> + call(decode_null, [BytesVar,TagStr]); + 'OBJECT IDENTIFIER' -> + call(decode_object_identifier, [BytesVar,TagStr]); + 'RELATIVE-OID' -> + call(decode_relative_oid, [BytesVar,TagStr]); + 'OCTET STRING' -> + check_constraint(decode_octet_string, [BytesVar,TagStr], + Constraint, {erlang,byte_size}, identity); + restricted_string -> + check_constraint(decode_restricted_string, [BytesVar,TagStr], + Constraint, + {erlang,byte_size}, + fun(Val) -> + emit("binary_to_list("), + Val(), + emit(")") + end); + 'UniversalString' -> + check_constraint(decode_universal_string, [BytesVar,TagStr], + Constraint, {erlang,length}, identity); + 'UTF8String' -> + call(decode_UTF8_string, [BytesVar,TagStr]); + 'BMPString' -> + check_constraint(decode_BMP_string, [BytesVar,TagStr], + Constraint, {erlang,length}, identity); + 'ASN1_OPEN_TYPE' -> + call(decode_open_type_as_binary, [BytesVar,TagStr]) end. %% Simplify an integer constraint so that we can efficiently test it. @@ -587,7 +608,7 @@ int_constr(C) -> [{'ValueRange',{_,_}=Range}] -> Range; [{'SingleValue',Sv}] -> - {Sv,Sv}; + Sv; [] -> [] end. @@ -598,16 +619,108 @@ gen_dec_bit_string(BytesVar, _Constraint, [_|_]=NNL, TagStr) -> gen_dec_bit_string(BytesVar, Constraint, [], TagStr) -> case asn1ct:get_bit_string_format() of compact -> - call(decode_compact_bit_string, - [BytesVar,{asis,Constraint},TagStr]); + check_constraint(decode_compact_bit_string, + [BytesVar,TagStr], + Constraint, + {ber,compact_bit_string_size}, + identity); legacy -> - call(decode_legacy_bit_string, - [BytesVar,{asis,Constraint},TagStr]); + check_constraint(decode_native_bit_string, + [BytesVar,TagStr], + Constraint, + {erlang,bit_size}, + fun(Val) -> + asn1ct_name:new(val), + emit([{curr,val}," = "]), + Val(), + emit([com,nl, + {call,ber,native_to_legacy_bit_string, + [{curr,val}]}]) + end); bitstring -> - call(decode_native_bit_string, - [BytesVar,{asis,Constraint},TagStr]) + check_constraint(decode_native_bit_string, + [BytesVar,TagStr], + Constraint, + {erlang,bit_size}, + identity) + end. + +check_constraint(F, Args, Constr, PreConstr0, ReturnVal0) -> + PreConstr = case PreConstr0 of + identity -> + fun(V) -> V end; + {Mod,Name} -> + fun(V) -> + asn1ct_name:new(c), + emit([{curr,c}," = ", + {call,Mod,Name,[V]},com,nl]), + {curr,c} + end + end, + ReturnVal = case ReturnVal0 of + identity -> fun(Val) -> Val() end; + _ -> ReturnVal0 + end, + case Constr of + [] when ReturnVal0 =:= identity -> + %% No constraint, no complications. + call(F, Args); + [] -> + %% No constraint, but the return value could consist + %% of more than one statement. + emit(["begin",nl]), + ReturnVal(fun() -> call(F, Args) end), + emit([nl, + "end",nl]); + _ -> + %% There is a constraint. + asn1ct_name:new(val), + emit(["begin",nl, + {curr,val}," = ",{call,ber,F,Args},com,nl]), + PreVal0 = asn1ct_gen:mk_var(asn1ct_name:curr(val)), + PreVal = PreConstr(PreVal0), + emit("if "), + case Constr of + {Min,Max} -> + emit([{asis,Min}," =< ",PreVal,", ", + PreVal," =< ",{asis,Max}]); + Sv when is_integer(Sv) -> + emit([PreVal," =:= ",{asis,Sv}]) + end, + emit([" ->",nl]), + ReturnVal(fun() -> emit(PreVal0) end), + emit([";",nl, + "true ->",nl, + "exit({error,{asn1,bad_range}})",nl, + "end",nl, + "end"]) end. +gen_dec_enumerated(BytesVar, NNL0, TagStr) -> + asn1ct_name:new(enum), + emit(["case ", + {call,ber,decode_integer,[BytesVar,TagStr]}, + " of",nl]), + NNL = case NNL0 of + {L1,L2} -> + L1 ++ L2 ++ [accept]; + [_|_] -> + NNL0 ++ [error] + end, + gen_dec_enumerated_1(NNL), + emit("end"). + +gen_dec_enumerated_1([accept]) -> + asn1ct_name:new(default), + emit([{curr,default}," -> {asn1_enum,",{curr,default},"}",nl]); +gen_dec_enumerated_1([error]) -> + asn1ct_name:new(default), + emit([{curr,default}," -> exit({error,{asn1,{illegal_enumerated,", + {curr,default},"}}})",nl]); +gen_dec_enumerated_1([{V,K}|T]) -> + emit([{asis,K}," -> ",{asis,V},";",nl]), + gen_dec_enumerated_1(T). + %% Object code generating for encoding and decoding %% ------------------------------------------------ @@ -952,9 +1065,8 @@ gen_decode_field_call(ObjName,FieldName,Bytes,Type) -> Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag], case Type#typedef.name of - {primitive,bif} -> %%tag should be the primitive tag - gen_dec_prim(ber,Def,Bytes,Tag,"TagIn",?PRIMITIVE, - opt_or_default), + {primitive,bif} -> + gen_dec_prim(Def, Bytes, Tag), []; {constructed,bif} -> emit({" 'dec_",ObjName,'_',FieldName, @@ -982,8 +1094,7 @@ gen_decode_default_call(ClassName,FieldName,Bytes,Type) -> FieldName])), typespec=Type}]; {primitive,bif} -> - gen_dec_prim(ber,Type,Bytes,Tag,"TagIn", - ?PRIMITIVE,opt_or_default), + gen_dec_prim(Type, Bytes, Tag), []; #'Externaltypereference'{module=CurrentMod,type=Etype} -> emit([" 'dec_",Etype,"'(",Bytes, " ,",{asis,Tag},")",nl]), @@ -1090,13 +1201,11 @@ gen_objset_enc(Erules, ObjSetName, UniqueName, %% See X.681 Annex E for the following case gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, _ClFields,_NthObj,Acc) -> - emit(["'getenc_",ObjSetName,"'(_) ->",nl]), - emit({indent(3),"fun(_, Val, _RestPrimFieldName) ->",nl}), - emit({indent(6),"Len = case Val of",nl,indent(9), - "Bin when is_binary(Bin) -> byte_size(Bin);",nl,indent(9), - "_ -> length(Val)",nl,indent(6),"end,"}), - emit({indent(6),"{Val,Len}",nl}), - emit({indent(3),"end.",nl,nl}), + emit(["'getenc_",ObjSetName,"'(_) ->",nl, + indent(2),"fun(_, Val, _RestPrimFieldName) ->",nl]), + emit_enc_open_type(4), + emit([nl, + indent(2),"end.",nl,nl]), Acc; gen_objset_enc(_, ObjSetName, UniqueName, [], _, _, _, Acc) -> emit_default_getenc(ObjSetName, UniqueName), @@ -1158,13 +1267,8 @@ gen_inlined_enc_funs1(Fields, [{typefield,Name,_}|Rest], ObjSetName, %% were no type in the table and we therefore generate %% code that returns the input for application %% treatment. - emit([indent(9),{asis,Name}," ->",nl, - indent(12),"Len = case Val of",nl, - indent(15),"Bin when is_binary(Bin) -> " - "byte_size(Bin);",nl, - indent(15),"_ -> length(Val)",nl, - indent(12),"end,",nl, - indent(12),"{Val,Len}"]), + emit([indent(9),{asis,Name}," ->",nl]), + emit_enc_open_type(11), {Acc0,0} end, gen_inlined_enc_funs1(Fields, Rest, ObjSetName, Sep, NthObj+NAdd, Acc); @@ -1175,6 +1279,25 @@ gen_inlined_enc_funs1(_, [], _, _, NthObj, Acc) -> indent(3),"end"]), {Acc,NthObj}. +emit_enc_open_type(I) -> + Indent = indent(I), + S = [Indent, "case Val of",nl, + Indent,indent(2),"{asn1_OPENTYPE,Bin} when is_binary(Bin) ->",nl, + Indent,indent(4),"{Bin,byte_size(Bin)}"| + case asn1ct:use_legacy_types() of + false -> + [nl, + Indent,"end"]; + true -> + [";",nl, + Indent,indent(2),"Bin when is_binary(Bin) ->",nl, + Indent,indent(4),"{Bin,byte_size(Bin)};",nl, + Indent,indent(2),"_ ->",nl, + Indent,indent(4),"{Val,length(Val)}",nl, + Indent, "end"] + end], + emit(S). + emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type}, InternalDefFunName) -> OTag = Type#type.tag, @@ -1258,14 +1381,9 @@ gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName, _ClFields,_NthObj) -> emit(["'getdec_",ObjSetName,"'(_) ->",nl]), emit([indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]), - - emit([indent(4),"case Bytes of",nl, - indent(6),"Bin when is_binary(Bin) -> ",nl, - indent(8),"Bin;",nl, - indent(6),"_ ->",nl, - indent(8),{call,ber,ber_encode,["Bytes"]},nl, - indent(4),"end",nl]), - emit([indent(2),"end.",nl,nl]), + emit_dec_open_type(4), + emit([nl, + indent(2),"end.",nl,nl]), ok; gen_objset_dec(_, ObjSetName, UniqueName, [], _, _, _) -> emit_default_getdec(ObjSetName, UniqueName), @@ -1312,12 +1430,8 @@ gen_inlined_dec_funs1(Fields, [{typefield,Name,Prop}|Rest], end, 0; false -> - emit([indent(9),{asis,Name}," ->",nl, - indent(12),"Len = case Bytes of",nl, - indent(15),"B when is_binary(B) -> byte_size(B);",nl, - indent(15),"_ -> length(Bytes)",nl, - indent(12),"end,",nl, - indent(12),"{Bytes,[],Len}"]), + emit([indent(9),{asis,Name}," ->",nl]), + emit_dec_open_type(11), 0 end, gen_inlined_dec_funs1(Fields, Rest, ObjSetName, Sep, NthObj+N); @@ -1328,7 +1442,28 @@ gen_inlined_dec_funs1(_, [], _, _, NthObj) -> indent(3),"end"]), NthObj. -emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},Prop, +emit_dec_open_type(I) -> + Indent = indent(I), + S = case asn1ct:use_legacy_types() of + false -> + [Indent, "case Bytes of",nl, + Indent,indent(2),"Bin when is_binary(Bin) -> ",nl, + Indent,indent(4),"{asn1_OPENTYPE,Bin};",nl, + Indent,indent(2),"_ ->",nl, + Indent,indent(4),"{asn1_OPENTYPE,", + {call,ber,ber_encode,["Bytes"]},"}",nl, + Indent, "end"]; + true -> + [Indent, "case Bytes of",nl, + Indent,indent(2),"Bin when is_binary(Bin) -> ",nl, + Indent,indent(4),"Bin;",nl, + Indent,indent(2),"_ ->",nl, + Indent,indent(4),{call,ber,ber_encode,["Bytes"]},nl, + Indent, "end"] + end, + emit(S). + +emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type}, _Prop, InternalDefFunName) -> OTag = Type#type.tag, %% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag], @@ -1336,8 +1471,7 @@ emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},Prop, case {ExtName,Name} of {primitive,bif} -> emit(indent(12)), - gen_dec_prim(ber,Type,"Bytes",Tag,"TagIn", - ?PRIMITIVE,Prop), + gen_dec_prim(Type, "Bytes", Tag), 0; {constructed,bif} -> emit([indent(12),"'dec_", @@ -1354,7 +1488,7 @@ emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},Prop, emit_inner_of_decfun(#typedef{name=Name},_Prop,_) -> emit([indent(12),"'dec_",Name,"'(Bytes)"]), 0; -emit_inner_of_decfun(Type,Prop,_) when is_record(Type,type) -> +emit_inner_of_decfun(#type{}=Type, _Prop, _) -> OTag = Type#type.tag, %% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag], Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag], @@ -1365,8 +1499,7 @@ emit_inner_of_decfun(Type,Prop,_) when is_record(Type,type) -> case WhatKind of {primitive,bif} -> emit([indent(9),Def," ->",nl,indent(12)]), - gen_dec_prim(ber,Type,"Bytes",Tag,"TagIn", - ?PRIMITIVE,Prop); + gen_dec_prim(Type, "Bytes", Tag); #'Externaltypereference'{module=CurrMod,type=T} -> emit([indent(9),T," ->",nl,indent(12),"'dec_",T, % "'(Bytes, ",Prop,")"]); @@ -1503,6 +1636,3 @@ extaddgroup2sequence(ExtList) when is_list(ExtList) -> call(F, Args) -> asn1ct_func:call(ber, F, Args). - -need(F, Arity) -> - asn1ct_func:need({ber,F,Arity}). diff --git a/lib/asn1/src/asn1ct_gen_check.erl b/lib/asn1/src/asn1ct_gen_check.erl new file mode 100644 index 0000000000..d80a02dfbf --- /dev/null +++ b/lib/asn1/src/asn1ct_gen_check.erl @@ -0,0 +1,271 @@ +%% vim: tabstop=8:shiftwidth=4 +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2014. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% + +-module(asn1ct_gen_check). +-export([emit/3]). + +-import(asn1ct_gen, [emit/1]). +-include("asn1_records.hrl"). + +emit(Type, Default, Value) -> + Key = {Type,Default}, + Gen = fun(Fd, Name) -> + file:write(Fd, gen(Name, Type, Default)) + end, + emit(" case "), + asn1ct_func:call_gen("is_default_", Key, Gen, [Value]), + emit([" of",nl, + "true -> {[],0};",nl, + "false ->",nl]). + +gen(Name, #type{def=T}, Default) -> + NameStr = atom_to_list(Name), + [NameStr,"(asn1_DEFAULT) ->\n", + "true;\n"|case do_gen(T, Default) of + {literal,Literal} -> + [NameStr,"(",term2str(Literal),") ->\n","true;\n", + NameStr,"(_) ->\n","false.\n\n"]; + {exception,Func,Args} -> + [NameStr,"(Value) ->\n", + "try ",Func,"(Value",arg2str(Args),") of\n", + "_ -> true\n" + "catch throw:false -> false\n" + "end.\n\n"] + end]. + +do_gen(_, asn1_NOVALUE) -> + {literal,asn1_NOVALUE}; +do_gen(#'Externaltypereference'{module=M,type=T}, Default) -> + #typedef{typespec=#type{def=Td}} = asn1_db:dbget(M, T), + do_gen(Td, Default); +do_gen('BOOLEAN', Default) -> + {literal,Default}; +do_gen({'BIT STRING',[]}, Default) -> + true = is_bitstring(Default), %Assertion. + case asn1ct:use_legacy_types() of + false -> + {literal,Default}; + true -> + {exception,need(check_legacy_bitstring, 2),[Default]} + end; +do_gen({'BIT STRING',[_|_]=NBL}, Default) -> + do_named_bitstring(NBL, Default); +do_gen({'ENUMERATED',_}, Default) -> + {literal,Default}; +do_gen('INTEGER', Default) -> + {literal,Default}; +do_gen({'INTEGER',NNL}, Default) -> + {exception,need(check_int, 3),[Default,NNL]}; +do_gen('NULL', Default) -> + {literal,Default}; +do_gen('OCTET STRING', Default) -> + true = is_binary(Default), %Assertion. + case asn1ct:use_legacy_types() of + false -> + {literal,Default}; + true -> + {exception,need(check_octetstring, 2),[Default]} + end; +do_gen('OBJECT IDENTIFIER', Default0) -> + Default = pre_process_oid(Default0), + {exception,need(check_objectidentifier, 2),[Default]}; +do_gen({'CHOICE',Cs}, Default) -> + {Tag,Value} = Default, + [Type] = [Type || #'ComponentType'{name=T,typespec=Type} <- Cs, + T =:= Tag], + case do_gen(Type#type.def, Value) of + {literal,Lit} -> + {literal,{Tag,Lit}}; + {exception,Func0,Args} -> + Key = {Tag,Func0,Args}, + Gen = fun(Fd, Name) -> + S = gen_choice(Name, Tag, Func0, Args), + ok = file:write(Fd, S) + end, + Func = asn1ct_func:call_gen("is_default_choice", Key, Gen), + {exception,atom_to_list(Func),[]} + end; +do_gen(#'SEQUENCE'{components=Cs}, Default) -> + do_seq_set(Cs, Default); +do_gen({'SEQUENCE OF',Type}, Default) -> + do_sof(Type, Default); +do_gen(#'SET'{components=Cs}, Default) -> + do_seq_set(Cs, Default); +do_gen({'SET OF',Type}, Default) -> + do_sof(Type, Default); +do_gen(Type, Default) -> + case asn1ct_gen:unify_if_string(Type) of + restrictedstring -> + {exception,need(check_restrictedstring, 2),[Default]}; + _ -> + %% Open type. Do our best. + {literal,Default} + end. + +do_named_bitstring(NBL, Default0) when is_list(Default0) -> + Default = lists:sort(Default0), + Bs = asn1ct_gen:named_bitstring_value(Default, NBL), + Func = case asn1ct:use_legacy_types() of + false -> check_named_bitstring; + true -> check_legacy_named_bitstring + end, + {exception,need(Func, 4),[Default,Bs,bit_size(Bs)]}; +do_named_bitstring(_, Default) when is_bitstring(Default) -> + Func = case asn1ct:use_legacy_types() of + false -> check_named_bitstring; + true -> check_legacy_named_bitstring + end, + {exception,need(Func, 3),[Default,bit_size(Default)]}. + +do_seq_set(Cs0, Default) -> + Tag = element(1, Default), + Cs1 = [T || #'ComponentType'{typespec=T} <- Cs0], + Cs = components(Cs1, tl(tuple_to_list(Default))), + case are_all_literals(Cs) of + true -> + Literal = list_to_tuple([Tag|[L || {literal,L} <- Cs]]), + {literal,Literal}; + false -> + Key = {Cs,Default}, + Gen = fun(Fd, Name) -> + S = gen_components(Name, Tag, Cs), + ok = file:write(Fd, S) + end, + Func = asn1ct_func:call_gen("is_default_cs_", Key, Gen), + {exception,atom_to_list(Func),[]} + end. + +do_sof(Type, Default0) -> + Default = lists:sort(Default0), + Cs0 = lists:duplicate(length(Default), Type), + Cs = components(Cs0, Default), + case are_all_literals(Cs) of + true -> + Literal = [Lit || {literal,Lit} <- Cs], + {exception,need(check_literal_sof, 2),[Literal]}; + false -> + Key = Cs, + Gen = fun(Fd, Name) -> + S = gen_sof(Name, Cs), + ok = file:write(Fd, S) + end, + Func = asn1ct_func:call_gen("is_default_sof", Key, Gen), + {exception,atom_to_list(Func),[]} + end. + +are_all_literals([{literal,_}|T]) -> + are_all_literals(T); +are_all_literals([_|_]) -> + false; +are_all_literals([]) -> true. + +gen_components(Name, Tag, Cs) -> + [atom_to_list(Name),"(Value) ->\n", + "case Value of\n", + "{",term2str(Tag)|gen_cs_1(Cs, 1, [])]. + +gen_cs_1([{literal,Lit}|T], I, Acc) -> + [",\n",term2str(Lit)|gen_cs_1(T, I, Acc)]; +gen_cs_1([H|T], I, Acc) -> + Var = "E"++integer_to_list(I), + [",\n",Var|gen_cs_1(T, I+1, [{Var,H}|Acc])]; +gen_cs_1([], _, Acc) -> + ["} ->\n"|gen_cs_2(Acc, "")]. + +gen_cs_2([{Var,{exception,Func,Args}}|T], Sep) -> + [Sep,Func,"(",Var,arg2str(Args),")"|gen_cs_2(T, ",\n")]; +gen_cs_2([], _) -> + [";\n", + "_ ->\n" + "throw(false)\n" + "end.\n"]. + +gen_sof(Name, Cs) -> + [atom_to_list(Name),"(Value) ->\n", + "case length(Value) of\n", + integer_to_list(length(Cs))," -> ok;\n" + "_ -> throw(false)\n" + "end,\n" + "T0 = lists:sort(Value)"|gen_sof_1(Cs, 1)]. + +gen_sof_1([{exception,Func,Args}|Cs], I) -> + NumStr = integer_to_list(I), + H = "H" ++ NumStr, + T = "T" ++ NumStr, + Prev = "T" ++ integer_to_list(I-1), + [",\n", + "[",H,case Cs of + [] -> []; + [_|_] -> ["|",T] + end,"] = ",Prev,",\n", + Func,"(",H,arg2str(Args),")"|gen_sof_1(Cs, I+1)]; +gen_sof_1([], _) -> + ".\n". + +components([#type{def=Def}|Ts], [V|Vs]) -> + [do_gen(Def, V)|components(Ts, Vs)]; +components([], []) -> []. + +gen_choice(Name, Tag, Func, Args) -> + NameStr = atom_to_list(Name), + [NameStr,"({",term2str(Tag),",Value}) ->\n" + " ",Func,"(Value",arg2str(Args),");\n", + NameStr,"(_) ->\n" + " throw(false).\n"]. + +pre_process_oid(Oid) -> + Reserved = reserved_oid(), + pre_process_oid(tuple_to_list(Oid), Reserved, []). + +pre_process_oid([H|T]=Tail, Res0, Acc) -> + case lists:keyfind(H, 2, Res0) of + false -> + {lists:reverse(Acc),Tail}; + {Names0,H,Res} -> + Names = case is_list(Names0) of + false -> [Names0]; + true -> Names0 + end, + Keys = [H|Names], + pre_process_oid(T, Res, [Keys|Acc]) + end. + +reserved_oid() -> + [{['itu-t',ccitt],0, + [{recommendation,0,[]}, + {question,1,[]}, + {administration,2,[]}, + {'network-operator',3,[]}, + {'identified-organization',4,[]}]}, + {iso,1,[{standard,0,[]}, + {'member-body',2,[]}, + {'identified-organization',3,[]}]}, + {['joint-iso-itu-t','joint-iso-ccitt'],2,[]}]. + +arg2str(Args) -> + [", "++term2str(Arg) || Arg <- Args]. + +term2str(T) -> + io_lib:format("~w", [T]). + +need(F, A) -> + asn1ct_func:need({check,F,A}), + atom_to_list(F). diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 8b999ddbf0..39cc0536f8 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -32,6 +32,7 @@ -export([gen_encode/2, gen_encode/3]). -export([gen_dec_external/2]). -export([extaddgroup2sequence/1]). +-export([dialyzer_suppressions/1]). -import(asn1ct_gen, [emit/1,demit/1]). -import(asn1ct_func, [call/3]). @@ -40,6 +41,15 @@ %% Generate ENCODING ****************************** %%****************************************x +dialyzer_suppressions(Erules) -> + case asn1ct_func:is_used({Erules,complete,1}) of + false -> + ok; + true -> + emit([" _ = complete(Arg),",nl]) + end, + emit([" ok.",nl]). + gen_encode(Erules,Type) when is_record(Type,typedef) -> gen_encode_user(Erules,Type). @@ -99,7 +109,7 @@ gen_encode_user(Erules,D) when is_record(D,typedef) -> gen_encode_prim(Erules, D) -> - Value = asn1ct_gen:mk_var(asn1ct_name:curr(val)), + Value = {var,atom_to_list(asn1ct_gen:mk_var(asn1ct_name:curr(val)))}, gen_encode_prim(Erules, D, Value). gen_encode_prim(Erules, #type{}=D, Value) -> @@ -132,7 +142,14 @@ gen_encode_prim_imm(Val, #type{def=Type0,constraint=Constraint}, Aligned) -> ToBinary = {real_common,encode_real}, asn1ct_imm:per_enc_restricted_string(Val, ToBinary, Aligned); {'BIT STRING',NNL} -> - asn1ct_imm:per_enc_bit_string(Val, NNL, Constraint, Aligned); + case asn1ct:use_legacy_types() of + false -> + asn1ct_imm:per_enc_bit_string(Val, NNL, + Constraint, Aligned); + true -> + asn1ct_imm:per_enc_legacy_bit_string(Val, NNL, + Constraint, Aligned) + end; 'NULL' -> asn1ct_imm:per_enc_null(Val, Aligned); 'OBJECT IDENTIFIER' -> @@ -144,15 +161,21 @@ gen_encode_prim_imm(Val, #type{def=Type0,constraint=Constraint}, Aligned) -> 'BOOLEAN' -> asn1ct_imm:per_enc_boolean(Val, Aligned); 'OCTET STRING' -> - asn1ct_imm:per_enc_octet_string(Val, Constraint, Aligned); + case asn1ct:use_legacy_types() of + false -> + asn1ct_imm:per_enc_octet_string(Val, Constraint, Aligned); + true -> + asn1ct_imm:per_enc_legacy_octet_string(Val, Constraint, + Aligned) + end; 'ASN1_OPEN_TYPE' -> case Constraint of [#'Externaltypereference'{type=Tname}] -> EncFunc = enc_func(Tname), - Imm = [{apply,EncFunc,[{expr,Val}]}], + Imm = [{apply,{local,EncFunc,[]},[Val]}], asn1ct_imm:per_enc_open_type(Imm, Aligned); [] -> - Imm = [{call,erlang,iolist_to_binary,[{expr,Val}]}], + Imm = [{call,erlang,iolist_to_binary,[Val]}], asn1ct_imm:per_enc_open_type(Imm, Aligned) end end. @@ -325,7 +348,10 @@ gen_dec_imm_1('GeneralizedTime', Constraint, Aligned) -> gen_dec_imm_1('OCTET STRING', Constraint, Aligned) -> SzConstr = asn1ct_imm:effective_constraint(bitstring, Constraint), Imm = asn1ct_imm:per_dec_octet_string(SzConstr, Aligned), - {convert,binary_to_list,Imm}; + case asn1ct:use_legacy_types() of + false -> {convert,{binary,copy},Imm}; + true -> {convert,binary_to_list,Imm} + end; gen_dec_imm_1('TeletexString', _Constraint, Aligned) -> gen_dec_restricted_string(Aligned); gen_dec_imm_1('T61String', _Constraint, Aligned) -> diff --git a/lib/asn1/src/asn1ct_imm.erl b/lib/asn1/src/asn1ct_imm.erl index 047156fc10..bdd14871d1 100644 --- a/lib/asn1/src/asn1ct_imm.erl +++ b/lib/asn1/src/asn1ct_imm.erl @@ -26,17 +26,19 @@ per_dec_octet_string/2,per_dec_open_type/1,per_dec_real/1, per_dec_restricted_string/1]). -export([per_dec_constrained/3,per_dec_normally_small_number/1]). --export([per_enc_bit_string/4,per_enc_boolean/2, +-export([per_enc_bit_string/4,per_enc_legacy_bit_string/4, + per_enc_boolean/2, per_enc_choice/3,per_enc_enumerated/3, per_enc_integer/3,per_enc_integer/4, per_enc_null/2, per_enc_k_m_string/4,per_enc_octet_string/3, + per_enc_legacy_octet_string/3, per_enc_open_type/2, per_enc_restricted_string/3, per_enc_small_number/2]). -export([per_enc_extension_bit/2,per_enc_extensions/4,per_enc_optional/3]). -export([per_enc_sof/5]). --export([enc_absent/3,enc_append/1,enc_bind_var/1]). +-export([enc_absent/3,enc_append/1,enc_element/2]). -export([enc_cg/2]). -export([optimize_alignment/1,optimize_alignment/2, dec_slim_cg/2,dec_code_gen/2]). @@ -80,15 +82,8 @@ per_dec_enumerated(NamedList0, Aligned) -> Ub = length(NamedList0) - 1, Constraint = [{'ValueRange',{0,Ub}}], Int = per_dec_integer(Constraint, Aligned), - EnumTail = case matched_range(Int) of - {0,Ub} -> - %% The error case can never happen. - []; - _ -> - [enum_error] - end, - NamedList = per_dec_enumerated_fix_list(NamedList0, EnumTail, 0), - {map,Int,NamedList}. + NamedList = per_dec_enumerated_fix_list(NamedList0, [enum_error], 0), + {map,Int,opt_map(NamedList, Int)}. per_dec_enumerated(BaseNamedList, NamedListExt0, Aligned) -> Base = per_dec_enumerated(BaseNamedList, Aligned), @@ -122,7 +117,7 @@ per_dec_length(no, AllowZero, Aligned) -> per_dec_named_integer(Constraint, NamedList0, Aligned) -> Int = per_dec_integer(Constraint, Aligned), NamedList = [{K,V} || {V,K} <- NamedList0] ++ [integer_default], - {map,Int,NamedList}. + {map,Int,opt_map(NamedList, Int)}. per_dec_k_m_string(StringType, Constraint, Aligned) -> SzConstr = effective_constraint(bitstring, Constraint), @@ -157,7 +152,37 @@ per_dec_restricted_string(Aligned) -> %%% Encoding. %%% -per_enc_bit_string(Val0, [], Constraint0, Aligned) -> +per_enc_bit_string(Val, [], Constraint0, Aligned) -> + {B,[[],Bits]} = mk_vars([], [bits]), + Constraint = effective_constraint(bitstring, Constraint0), + B ++ [{call,erlang,bit_size,[Val],Bits}| + per_enc_length(Val, 1, Bits, Constraint, Aligned, 'BIT STRING')]; +per_enc_bit_string(Val0, NNL0, Constraint0, Aligned) -> + {B,[Val,Bs,Bits,Positions]} = mk_vars(Val0, [bs,bits,positions]), + NNL = lists:keysort(2, NNL0), + Constraint = effective_constraint(bitstring, Constraint0), + ExtraArgs = case constr_min_size(Constraint) of + no -> []; + Lb -> [Lb] + end, + ToBs = case ExtraArgs of + [] -> + {call,per_common,bs_drop_trailing_zeroes,[Val]}; + [0] -> + {call,per_common,bs_drop_trailing_zeroes,[Val]}; + [Lower] -> + {call,per_common,adjust_trailing_zeroes,[Val,Lower]} + end, + B ++ [{'try', + [bit_string_name2pos_fun(NNL, Val)], + {Positions, + [{call,per_common,bitstring_from_positions, + [Positions|ExtraArgs]}]}, + [ToBs],Bs}, + {call,erlang,bit_size,[Bs],Bits}| + per_enc_length(Bs, 1, Bits, Constraint, Aligned, 'BIT STRING')]. + +per_enc_legacy_bit_string(Val0, [], Constraint0, Aligned) -> {B,[Val,Bs,Bits]} = mk_vars(Val0, [bs,bits]), Constraint = effective_constraint(bitstring, Constraint0), ExtraArgs = case constr_min_size(Constraint) of @@ -167,12 +192,13 @@ per_enc_bit_string(Val0, [], Constraint0, Aligned) -> B ++ [{call,per_common,to_bitstring,[Val|ExtraArgs],Bs}, {call,erlang,bit_size,[Bs],Bits}| per_enc_length(Bs, 1, Bits, Constraint, Aligned, 'BIT STRING')]; -per_enc_bit_string(Val0, NNL0, Constraint0, Aligned) -> +per_enc_legacy_bit_string(Val0, NNL0, Constraint0, Aligned) -> {B,[Val,Bs,Bits,Positions]} = mk_vars(Val0, [bs,bits,positions]), NNL = lists:keysort(2, NNL0), Constraint = effective_constraint(bitstring, Constraint0), ExtraArgs = case constr_min_size(Constraint) of no -> []; + 0 -> []; Lb -> [Lb] end, B ++ [{'try', @@ -235,10 +261,6 @@ per_enc_k_m_string(Val0, StringType, Constraint, Aligned) -> SzConstraint = effective_constraint(bitstring, Constraint), Unit = string_num_bits(StringType, Constraint, Aligned), Chars0 = char_tab(Constraint, StringType, Unit), - Args = case enc_char_tab(Chars0) of - notab -> [Val,Unit]; - Chars -> [Val,Unit,Chars] - end, Enc = case Unit of 16 -> {call,per_common,encode_chars_16bit,[Val],Bin}; @@ -247,7 +269,15 @@ per_enc_k_m_string(Val0, StringType, Constraint, Aligned) -> 8 -> {call,erlang,list_to_binary,[Val],Bin}; _ -> - {call,per_common,encode_chars,Args,Bin} + case enc_char_tab(Chars0) of + notab -> + {call,per_common,encode_chars,[Val,Unit],Bin}; + {tab,Tab} -> + {call,per_common,encode_chars,[Val,Unit,Tab],Bin}; + {compact_map,Map} -> + {call,per_common,encode_chars_compact_map, + [Val,Unit,Map],Bin} + end end, case Unit of 8 -> @@ -256,35 +286,33 @@ per_enc_k_m_string(Val0, StringType, Constraint, Aligned) -> B ++ [{call,erlang,length,[Val],Len},Enc] end ++ per_enc_length(Bin, Unit, Len, SzConstraint, Aligned, k_m_string). -per_enc_open_type([], Aligned) -> - [{put_bits,1,8,unit(1, Aligned)},{put_bits,0,8,[1]}]; -per_enc_open_type([{'cond', - [['_', - {put_bits,0,0,_}, - {call,per_common,encode_unconstrained_number,_}=Call]]}], - Aligned) -> - %% We KNOW that encode_unconstrained_number/1 will return an IO list; - %% therefore the call to complete/1 can be replaced with a cheaper - %% call to iolist_to_binary/1. - {Dst,Imm} = per_enc_open_type_output([Call], []), - ToBin = {erlang,iolist_to_binary}, - Imm ++ per_enc_open_type(Dst, ToBin, Aligned); -per_enc_open_type([{call,erlang,iolist_to_binary,Args}], Aligned) -> - {_,[_,Bin,Len]} = mk_vars('dummy', [bin,len]), - [{call,erlang,iolist_to_binary,Args,Bin}, - {call,erlang,byte_size,[Bin],Len}|per_enc_length(Bin, 8, Len, Aligned)]; per_enc_open_type(Imm0, Aligned) -> - try - {Prefix,Imm1} = split_off_nonbuilding(Imm0), - Prefix ++ enc_open_type(Imm1, Aligned) - catch - throw:impossible -> - {Dst,Imm} = per_enc_open_type_output(Imm0, []), - ToBin = {enc_mod(Aligned),complete}, - Imm ++ per_enc_open_type(Dst, ToBin, Aligned) - end. + Imm = case Aligned of + true -> + %% Temporarily make the implicit 'align' done by + %% complete/1 explicit to facilitate later + %% optimizations: the absence of 'align' can be used + %% as an indication that complete/1 can be replaced + %% with a cheaper operation such as + %% iolist_to_binary/1. The redundant 'align' will be + %% optimized away later. + Imm0 ++ [{put_bits,0,0,[1,align]}]; + false -> + Imm0 + end, + {[],[[],Val,Len,Bin]} = mk_vars([], [output,len,bin]), + [{list,Imm,Val}, + {call,enc_mod(Aligned),complete,[Val],Bin}, + {call,erlang,byte_size,[Bin],Len}| + per_enc_length(Bin, 8, Len, Aligned)]. + +per_enc_octet_string(Bin, Constraint0, Aligned) -> + {B,[[],Len]} = mk_vars([], [len]), + Constraint = effective_constraint(bitstring, Constraint0), + B ++ [{call,erlang,byte_size,[Bin],Len}| + per_enc_length(Bin, 8, Len, Constraint, Aligned, 'OCTET STRING')]. -per_enc_octet_string(Val0, Constraint0, Aligned) -> +per_enc_legacy_octet_string(Val0, Constraint0, Aligned) -> {B,[Val,Bin,Len]} = mk_vars(Val0, [bin,len]), Constraint = effective_constraint(bitstring, Constraint0), B ++ [{call,erlang,iolist_to_binary,[Val],Bin}, @@ -316,28 +344,27 @@ per_enc_extensions(Val0, Pos0, NumBits, Aligned) when NumBits > 0 -> _ -> [{put_bits,Bitmap,NumBits,[1]}] end, B++[{call,per_common,extension_bitmap,[Val,Pos,Pos+NumBits],Bitmap}, - {'cond',[[{eq,Bitmap,0}], - ['_'|Length ++ PutBits]],{var,"Extensions"}}]. + {list,[{'cond',[[{eq,Bitmap,0}], + ['_'|Length ++ PutBits]]}], + {var,"Extensions"}}]. per_enc_optional(Val0, {Pos,DefVals}, _Aligned) when is_integer(Pos), is_list(DefVals) -> - Val1 = lists:concat(["element(",Pos,", ",Val0,")"]), - {B,[Val]} = mk_vars(Val1, []), + {B,Val} = enc_element(Pos, Val0), Zero = {put_bits,0,1,[1]}, One = {put_bits,1,1,[1]}, B++[{'cond', [[{eq,Val,DefVal},Zero] || DefVal <- DefVals] ++ [['_',One]]}]; per_enc_optional(Val0, {Pos,{call,M,F,A}}, _Aligned) when is_integer(Pos) -> - Val1 = lists:concat(["element(",Pos,", ",Val0,")"]), - {B,[Val,Tmp]} = mk_vars(Val1, [tmp]), + {B,Val} = enc_element(Pos, Val0), + {[],[[],Tmp]} = mk_vars([], [tmp]), Zero = {put_bits,0,1,[1]}, One = {put_bits,1,1,[1]}, B++[{call,M,F,[Val|A],Tmp}, {'cond', [[{eq,Tmp,true},Zero],['_',One]]}]; per_enc_optional(Val0, Pos, _Aligned) when is_integer(Pos) -> - Val1 = lists:concat(["element(",Pos,", ",Val0,")"]), - {B,[Val]} = mk_vars(Val1, []), + {B,Val} = enc_element(Pos, Val0), Zero = {put_bits,0,1,[1]}, One = {put_bits,1,1,[1]}, B++[{'cond',[[{eq,Val,asn1_NOVALUE},Zero], @@ -391,20 +418,22 @@ enc_append([H|T]) -> [{block,H}|enc_append(T)]; enc_append([]) -> []. -enc_bind_var(Val) -> - {B,[{var,Var}]} = mk_vars(Val, []), - {B,list_to_atom(Var)}. +enc_element(N, Val0) -> + {[],[Val,Dst]} = mk_vars(Val0, [element]), + {[{call,erlang,element,[N,Val],Dst}],Dst}. enc_cg(Imm0, false) -> Imm1 = enc_cse(Imm0), - Imm = enc_pre_cg(Imm1), + Imm2 = enc_pre_cg(Imm1), + Imm = enc_opt(Imm2), enc_cg(Imm); enc_cg(Imm0, true) -> Imm1 = enc_cse(Imm0), Imm2 = enc_hoist_align(Imm1), Imm3 = enc_opt_al(Imm2), Imm4 = per_fixup(Imm3), - Imm = enc_pre_cg(Imm4), + Imm5 = enc_pre_cg(Imm4), + Imm = enc_opt(Imm5), enc_cg(Imm). %%% @@ -552,14 +581,42 @@ per_num_bits(N) when N =< 64 -> 6; per_num_bits(N) when N =< 128 -> 7; per_num_bits(N) when N =< 255 -> 8. +opt_map(Map, Imm) -> + case matched_range(Imm) of + unknown -> Map; + {Lb,Ub} -> opt_map_1(Map, Lb, Ub) + end. + +opt_map_1([{I,_}=Pair|T], Lb, Ub) -> + if + I =:= Lb, I =< Ub -> + [Pair|opt_map_1(T, Lb+1, Ub)]; + Lb < I, I =< Ub -> + [Pair|opt_map_1(T, Lb, Ub)]; + true -> + opt_map_1(T, Lb, Ub) + end; +opt_map_1(Map, Lb, Ub) -> + if + Lb =< Ub -> + Map; + true -> + [] + end. + matched_range({get_bits,Bits0,[U|Flags]}) when is_integer(U) -> - case lists:member(signed, Flags) of - false -> + case not lists:member(signed, Flags) andalso is_integer(Bits0) of + true -> Bits = U*Bits0, {0,(1 bsl Bits) - 1}; - true -> + false -> unknown end; +matched_range({add,Imm,Add}) -> + case matched_range(Imm) of + unknown -> unknown; + {Lb,Ub} -> {Lb+Add,Ub+Add} + end; matched_range(_Op) -> unknown. string_num_bits(StringType, Constraint, Aligned) -> @@ -881,6 +938,9 @@ dcg_list_outside([{call,Fun,{V,Buf},{Dst,DstBuf}}|T]) -> emit(["{",Dst,",",DstBuf,"} = "]), Fun(V, Buf), iter_dcg_list_outside(T); +dcg_list_outside([{convert,{M,F},V,Dst}|T]) -> + emit([Dst," = ",{asis,M},":",{asis,F},"(",V,")"]), + iter_dcg_list_outside(T); dcg_list_outside([{convert,Op,V,Dst}|T]) -> emit([Dst," = ",Op,"(",V,")"]), iter_dcg_list_outside(T); @@ -972,11 +1032,11 @@ mk_dest(S) -> S. split_off_nonbuilding(Imm) -> lists:splitwith(fun is_nonbuilding/1, Imm). -is_nonbuilding({apply,_,_,_}) -> true; is_nonbuilding({assign,_,_}) -> true; is_nonbuilding({call,_,_,_,_}) -> true; -is_nonbuilding({'cond',_,_}) -> true; is_nonbuilding({lc,_,_,_,_}) -> true; +is_nonbuilding({set,_,_}) -> true; +is_nonbuilding({list,_,_}) -> true; is_nonbuilding({sub,_,_,_}) -> true; is_nonbuilding({'try',_,_,_,_}) -> true; is_nonbuilding(_) -> false. @@ -986,17 +1046,13 @@ mk_vars(Input0, Temps) -> Curr = asn1ct_name:curr(enc), [H|T] = atom_to_list(Curr), Base = [H - ($a - $A)|T ++ "@"], - if - is_atom(Input0) -> - Input = {var,atom_to_list(Input0)}, - {[],[Input|mk_vars_1(Base, Temps)]}; - is_integer(Input0) -> + case Input0 of + {var,Name} when is_list(Name) -> {[],[Input0|mk_vars_1(Base, Temps)]}; - Input0 =:= [] -> + [] -> {[],[Input0|mk_vars_1(Base, Temps)]}; - true -> - Input = mk_var(Base, input), - {[{assign,Input,Input0}],[Input|mk_vars_1(Base, Temps)]} + _ when is_integer(Input0) -> + {[],[Input0|mk_vars_1(Base, Temps)]} end. mk_vars_1(Base, Vars) -> @@ -1143,8 +1199,15 @@ per_enc_length(Bin, Unit, Len, {Lb,Ub}, Aligned, Type) U = unit(Unit, Aligned, Type, Lb*Unit, Ub*Unit), PutBits = [{put_bits,Bin,binary,U}], build_length_cond(Prefix, [[Check|PutLen++PutBits]]); -per_enc_length(Bin, Unit, Len, Sv, Aligned, Type) when is_integer(Sv) -> - NumBits = Sv*Unit, +per_enc_length(Bin, Unit0, Len, Sv, Aligned, Type) when is_integer(Sv) -> + NumBits = Sv*Unit0, + Unit = case NumBits rem 8 of + 0 -> + %% Help out the alignment optimizer. + 8; + _ -> + Unit0 + end, U = unit(Unit, Aligned, Type, NumBits, NumBits), Pb = {put_bits,Bin,binary,U}, [{'cond',[[{eq,Len,Sv},Pb]]}]. @@ -1254,6 +1317,8 @@ eval_cond_1({eq,[],[]}) -> true; eval_cond_1({eq,I,N}) when is_integer(I), is_integer(N) -> I =:= N; +eval_cond_1({ge,I,N}) when is_integer(I), is_integer(N) -> + I >= N; eval_cond_1({lt,I,N}) when is_integer(I), is_integer(N) -> I < N; eval_cond_1(_) -> maybe. @@ -1268,9 +1333,15 @@ prepend_to_cond_1([Check|T], Code) -> enc_char_tab(notab) -> notab; enc_char_tab(Tab0) -> - Tab = tuple_to_list(Tab0), - First = hd(Tab), - {First-1,list_to_tuple(enc_char_tab_1(Tab, First, 0))}. + Tab1 = tuple_to_list(Tab0), + First = hd(Tab1), + Tab = enc_char_tab_1(Tab1, First, 0), + case lists:member(ill, Tab) of + false -> + {compact_map,{First,tuple_size(Tab0)}}; + true -> + {tab,{First-1,list_to_tuple(Tab)}} + end. enc_char_tab_1([H|T], H, I) -> [I|enc_char_tab_1(T, H+1, I+1)]; @@ -1358,58 +1429,6 @@ opt_choice_2([_|_], _) -> throw(impossible); opt_choice_2([], _) -> []. - -%%% -%%% Helper functions for code generation of open types. -%%% - -per_enc_open_type(Val0, {ToBinMod,ToBinFunc}, Aligned) -> - {B,[Val,Len,Bin]} = mk_vars(Val0, [len,bin]), - B ++ [{call,ToBinMod,ToBinFunc,[Val],Bin}, - {call,erlang,byte_size,[Bin],Len}| - per_enc_length(Bin, 8, Len, Aligned)]. - -enc_open_type([{'cond',Cs}], Aligned) -> - [{'cond',[[C|enc_open_type_1(Act, Aligned)] || [C|Act] <- Cs]}]; -enc_open_type(_, _) -> - throw(impossible). - -enc_open_type_1([{error,_}]=Imm, _) -> - Imm; -enc_open_type_1(Imm, Aligned) -> - NumBits = num_bits(Imm, 0), - Pad = case 8 - (NumBits rem 8) of - 8 -> []; - Pad0 -> [{put_bits,0,Pad0,[1]}] - end, - NumBytes = (NumBits+7) div 8, - enc_length(NumBytes, no, Aligned) ++ Imm ++ Pad. - -num_bits([{put_bits,_,N,[U|_]}|T], Sum) when is_integer(N) -> - num_bits(T, Sum+N*U); -num_bits([_|_], _) -> - throw(impossible); -num_bits([], Sum) -> Sum. - -per_enc_open_type_output([{apply,F,A}], Acc) -> - Dst = output_var(), - {Dst,lists:reverse(Acc, [{apply,F,A,{var,atom_to_list(Dst)}}])}; -per_enc_open_type_output([{call,M,F,A}], Acc) -> - Dst = output_var(), - {Dst,lists:reverse(Acc, [{call,M,F,A,{var,atom_to_list(Dst)}}])}; -per_enc_open_type_output([{'cond',Cs}], Acc) -> - Dst = output_var(), - {Dst,lists:reverse(Acc, [{'cond',Cs,{var,atom_to_list(Dst)}}])}; -per_enc_open_type_output([H|T], Acc) -> - per_enc_open_type_output(T, [H|Acc]). - -output_var() -> - asn1ct_name:new(enc), - Curr = asn1ct_name:curr(enc), - [H|T] = atom_to_list(Curr), - list_to_atom([H - ($a - $A)|T ++ "@output"]). - - %%% %%% Optimize list comprehensions (SEQUENCE OF/SET OF). %%% @@ -1587,16 +1606,16 @@ collect_put_bits(Imm) -> %%% the same element twice. %%% -enc_cse([{assign,{var,V},E}=H|T]) -> - [H|enc_cse_1(T, E, V)]; +enc_cse([{call,erlang,element,Args,V}=H|T]) -> + [H|enc_cse_1(T, Args, V)]; enc_cse(Imm) -> Imm. -enc_cse_1([{assign,Dst,E}|T], E, V) -> - [{assign,Dst,V}|enc_cse_1(T, E, V)]; -enc_cse_1([{block,Bl}|T], E, V) -> - [{block,enc_cse_1(Bl, E, V)}|enc_cse_1(T, E, V)]; -enc_cse_1([H|T], E, V) -> - [H|enc_cse_1(T, E, V)]; +enc_cse_1([{call,erlang,element,Args,Dst}|T], Args, V) -> + [{set,V,Dst}|enc_cse_1(T, Args, V)]; +enc_cse_1([{block,Bl}|T], Args, V) -> + [{block,enc_cse_1(Bl, Args, V)}|enc_cse_1(T, Args, V)]; +enc_cse_1([H|T], Args, V) -> + [H|enc_cse_1(T, Args, V)]; enc_cse_1([], _, _) -> []. @@ -1637,7 +1656,7 @@ enc_pre_cg_2({block,Bl0}, StL, StB) -> enc_pre_cg_1(Bl0, StL, StB); enc_pre_cg_2({call,_,_,_}=Imm, _, _) -> Imm; -enc_pre_cg_2({call_gen,_,_,_,_}=Imm, _, _) -> +enc_pre_cg_2({call_gen,_,_,_,_,_}=Imm, _, _) -> Imm; enc_pre_cg_2({'cond',Cs0}, StL, _StB) -> Cs = [{C,enc_pre_cg_1(Act, StL, outside_seq)} || [C|Act] <- Cs0], @@ -1662,18 +1681,22 @@ enc_pre_cg_2({var,_}=Imm, _, _) -> Imm. enc_make_cons({binary,H}, {binary,T}) -> {binary,H++T}; enc_make_cons({binary,H0}, {cons,{binary,H1},T}) -> - {cons,{binary,H0++H1},T}; + enc_make_cons({binary,H0++H1}, T); +enc_make_cons({binary,H}, {cons,{integer,Int},T}) -> + enc_make_cons({binary,H++[{put_bits,Int,8,[1]}]}, T); enc_make_cons({integer,Int}, {binary,T}) -> {binary,[{put_bits,Int,8,[1]}|T]}; +enc_make_cons({integer,Int}, {cons,{binary,H},T}) -> + enc_make_cons({binary,[{put_bits,Int,8,[1]}|H]}, T); enc_make_cons(H, T) -> {cons,H,T}. -enc_pre_cg_nonbuilding({'cond',Cs0,Dst}, StL) -> - Cs = [{C,enc_pre_cg_1(Act, StL, outside_seq)} || [C|Act] <- Cs0], - {'cond',Cs,Dst}; enc_pre_cg_nonbuilding({lc,B0,Var,List,Dst}, StL) -> B = enc_pre_cg_1(B0, StL, outside_seq), {lc,B,Var,List,Dst}; +enc_pre_cg_nonbuilding({list,List0,Dst}, _StL) -> + List = enc_pre_cg_1(List0, outside_list, outside_seq), + {list,List,Dst}; enc_pre_cg_nonbuilding({'try',Try0,{P,Succ0},Else0,Dst}, StL) -> Try = enc_pre_cg_1(Try0, StL, outside_seq), Succ = enc_pre_cg_1(Succ0, StL, outside_seq), @@ -1681,6 +1704,562 @@ enc_pre_cg_nonbuilding({'try',Try0,{P,Succ0},Else0,Dst}, StL) -> {'try',Try,{P,Succ},Else,Dst}; enc_pre_cg_nonbuilding(Imm, _) -> Imm. +%%% +%%% Optimize calls to complete/1 and surrounding code. There are +%%% several opportunities for optimizations. +%%% +%%% It may be possible to replace the call to complete/1 with +%%% something cheaper (most important for the PER back-end which has +%%% an expensive complete/1 implementation). If we can be sure that +%%% complete/1 will be called with an iolist (no 'align' atoms or +%%% bitstrings in the list), we can call iolist_to_binary/1 +%%% instead. If the list may include bitstrings, we can can call +%%% list_to_bitstring/1 (note that list_to_bitstring/1 does not accept +%%% a binary or bitstring, so we MUST be sure that we only pass it a +%%% list). If complete/1 is called with a binary, we can omit the +%%% call altogether. +%%% +%%% A call to byte_size/1 that follows complete/1 can be eliminated +%%% if the size of the binary produced by complete/1 can be determined +%%% and is constant. +%%% +%%% The code that encodes the length descriptor (a 'cond' instruction) +%%% for a binary produced by complete/1 can be simplified if the lower +%%% and upper bounds for the size of the binary are known. +%%% + +-record(ost, + {sym, + t + }). + +enc_opt(Imm0) -> + {Imm,_} = enc_opt(Imm0, #ost{sym=gb_trees:empty()}), + Imm. + +enc_opt(align, St) -> + {align,St#ost{t=t_align({0,7})}}; +enc_opt({apply,What,As}, St) -> + {{apply,What,subst_list(As, St)},St#ost{t=t_any()}}; +enc_opt({assign,_,_}=Imm, St) -> + {Imm,St}; +enc_opt({binary,PutBits0}, St) -> + PutBits = [{put_bits,subst(V, St),Sz,F} || + {put_bits,V,Sz,F} <- PutBits0], + NumBits = lists:foldl(fun({put_bits,_,Bits,_}, Sum) -> + Sum+Bits + end, 0, PutBits), + {{binary,PutBits},St#ost{t=t_bitstring(NumBits)}}; +enc_opt({block,Bl0}, St0) -> + {Bl,St} = enc_opt(Bl0, St0), + {{block,Bl},St}; +enc_opt({call,binary,encode_unsigned,[Int],Bin}=Imm, St0) -> + Type = get_type(Int, St0), + St = case t_range(Type) of + any -> + set_type(Bin, t_binary(), St0); + {Lb0,Ub0} -> + Lb = bit_size(binary:encode_unsigned(Lb0)), + Ub = bit_size(binary:encode_unsigned(Ub0)), + set_type(Bin, t_binary({Lb,Ub}), St0) + end, + {Imm,St}; +enc_opt({call,erlang,bit_size,[Bin],Dst}=Imm0, St0) -> + Type = get_type(Bin, St0), + case t_range(Type) of + any -> + St1 = set_type(Bin, t_bitstring(), St0), + St = propagate(Dst, + fun(T, S) -> + bit_size_propagate(Bin, T, S) + end, St1), + {Imm0,St}; + {Lb,Ub}=Range -> + St = set_type(Dst, t_integer(Range), St0), + Imm = case Lb of + Ub -> none; + _ -> Imm0 + end, + {Imm,St} + end; +enc_opt({call,erlang,byte_size,[Bin],Dst}=Imm0, St0) -> + Type = get_type(Bin, St0), + case t_range(Type) of + any -> + St1 = set_type(Bin, t_binary(), St0), + St = propagate(Dst, + fun(T, S) -> + byte_size_propagate(Bin, T, S) + end, St1), + {Imm0,St}; + {Lb0,Ub0} -> + Lb = (Lb0+7) div 8, + Ub = (Ub0+7) div 8, + St = set_type(Dst, t_integer({Lb,Ub}), St0), + Imm = case Lb of + Ub -> none; + _ -> Imm0 + end, + {Imm,St} + end; +enc_opt({call,erlang,iolist_to_binary,_}=Imm, St) -> + {Imm,St#ost{t=t_binary()}}; +enc_opt({call,erlang,length,[List],Dst}=Imm0, St0) -> + St1 = propagate(Dst, + fun(T, S) -> + length_propagate(List, T, S) + end, St0), + {Imm0,St1}; +enc_opt({call,per,complete,[Data],Dst}, St0) -> + Type = get_type(Data, St0), + St = set_type(Dst, t_binary(t_range(Type)), St0), + case t_type(Type) of + binary -> + {{set,Data,Dst},St}; + bitlist -> + %% We KNOW that list_to_bitstring/1 will construct + %% a binary (the number of bits is divisible by 8) + %% because per_enc_open_type/2 added an 'align' atom + %% at the end. If that 'align' atom had not been + %% optimized away, the type would have been 'align' + %% instead of 'bitlist'. + {{call,erlang,list_to_bitstring,[Data],Dst},St}; + iolist -> + {{call,erlang,iolist_to_binary,[Data],Dst},St}; + nil -> + Imm = {list,{binary,[{put_bits,0,8,[1]}]},Dst}, + enc_opt(Imm, St0); + _ -> + {{call,per,complete,[Data],Dst},St} + end; +enc_opt({call,uper,complete,[Data],Dst}, St0) -> + Type = get_type(Data, St0), + St = set_type(Dst, t_binary(t_range(Type)), St0), + case t_type(Type) of + binary -> + {{set,Data,Dst},St0}; + iolist -> + {{call,erlang,iolist_to_binary,[Data],Dst},St}; + nil -> + Imm = {list,{binary,[{put_bits,0,8,[1]}]},Dst}, + enc_opt(Imm, St0); + _ -> + %% 'bitlist' or 'any'. + {{call,uper,complete,[Data],Dst},St} + end; +enc_opt({call,per_common,encode_chars,[List,NumBits|_],Dst}=Imm, St0) -> + %% Note: Never used when NumBits =:= 8 (list_to_binary/1 will + %% be used instead). + St1 = set_type(Dst, t_bitstring(), St0), + St = propagate(List, + fun(T, S) -> + char_propagate(Dst, T, NumBits, S) + end, St1), + {Imm,St}; +enc_opt({call,per_common,encode_chars_16bit,[List],Dst}=Imm, St0) -> + St1 = set_type(Dst, t_binary(), St0), + St = propagate(List, + fun(T, S) -> + char_propagate(Dst, T, 16, S) + end, St1), + {Imm,St}; +enc_opt({call,per_common,encode_big_chars,[List],Dst}=Imm, St0) -> + St1 = set_type(Dst, t_binary(), St0), + St = propagate(List, + fun(T, S) -> + char_propagate(Dst, T, 32, S) + end, St1), + {Imm,St}; +enc_opt({call,per_common,encode_fragmented,[_,Unit]}=Imm, St) -> + T = case Unit rem 8 of + 0 -> t_iolist(); + _ -> t_bitlist() + end, + {Imm,St#ost{t=T}}; +enc_opt({call,per_common,encode_unconstrained_number,_}=Imm, St) -> + {Imm,St#ost{t=t_iolist()}}; +enc_opt({call,per_common,bitstring_from_positions,_}=Imm, St) -> + {Imm,St#ost{t=t_bitstring()}}; +enc_opt({call,per_common,to_named_bitstring,_}=Imm, St) -> + {Imm,St#ost{t=t_bitstring()}}; +enc_opt({call,_,_,_}=Imm, St) -> + {Imm,St#ost{t=t_any()}}; +enc_opt({call,_,_,_,_}=Imm, St) -> + {Imm,St#ost{t=undefined}}; +enc_opt({call_gen,N,K,F,L,As}, St) -> + {{call_gen,N,K,F,L,subst(As, St)},St#ost{t=t_any()}}; +enc_opt({'cond',Cs0}, St0) -> + case enc_opt_cs(Cs0, St0) of + [{'_',Imm,Type}] -> + {Imm,St0#ost{t=Type}}; + [{Cond,Imm,Type0}|Cs1] -> + {Cs,Type} = enc_opt_cond_1(Cs1, Type0, [{Cond,Imm}]), + {{'cond',Cs},St0#ost{t=Type}} + end; +enc_opt({cons,H0,T0}, St0) -> + {H,#ost{t=TypeH}=St1} = enc_opt(H0, St0), + {T,#ost{t=TypeT}=St} = enc_opt(T0, St1), + {{cons,H,T},St#ost{t=t_cons(TypeH, TypeT)}}; +enc_opt({error,_}=Imm, St) -> + {Imm,St#ost{t=t_any()}}; +enc_opt({integer,V}, St) -> + {{integer,subst(V, St)},St#ost{t=t_integer()}}; +enc_opt({lc,E0,B,C}, St) -> + {E,_} = enc_opt(E0, St), + {{lc,E,B,C},St#ost{t=t_any()}}; +enc_opt({lc,E0,B,C,Dst}, St) -> + {E,_} = enc_opt(E0, St), + {{lc,E,B,C,Dst},St#ost{t=undefined}}; +enc_opt({list,Imm0,Dst}, St0) -> + {Imm,#ost{t=Type}=St1} = enc_opt(Imm0, St0), + St = set_type(Dst, Type, St1), + {{list,Imm,Dst},St#ost{t=undefined}}; +enc_opt(nil, St) -> + {nil,St#ost{t=t_nil()}}; +enc_opt({seq,H0,T0}, St0) -> + {H,St1} = enc_opt(H0, St0), + {T,St} = enc_opt(T0, St1), + case {H,T} of + {none,_} -> + {T,St}; + {{list,Imm,Data}, + {seq,{call,per,complete,[Data],_},_}} -> + %% Get rid of any explicit 'align' added by per_enc_open_type/2. + {{seq,{list,remove_trailing_align(Imm),Data},T},St}; + {_,_} -> + {{seq,H,T},St} + end; +enc_opt({set,_,_}=Imm, St) -> + {Imm,St#ost{t=undefined}}; +enc_opt({sub,Src0,Int,Dst}, St0) -> + Src = subst(Src0, St0), + Type = get_type(Src, St0), + St = case t_range(Type) of + any -> + propagate(Dst, + fun(T, S) -> + set_type(Src, t_add(T, Int), S) + end, + St0); + {Lb,Ub} -> + set_type(Dst, t_integer({Lb-Int,Ub-Int}), St0) + end, + {{sub,Src,Int,Dst},St#ost{t=undefined}}; +enc_opt({'try',Try0,{P,Succ0},Else0,Dst}, St0) -> + {Try,_} = enc_opt(Try0, St0), + {Succ,_} = enc_opt(Succ0, St0), + {Else,_} = enc_opt(Else0, St0), + {{'try',Try,{P,Succ},Else,Dst},St0#ost{t=undefined}}; +enc_opt({var,_}=Imm, St) -> + Type = get_type(Imm, St), + {subst(Imm, St),St#ost{t=Type}}. + +remove_trailing_align({block,Bl}) -> + {block,remove_trailing_align(Bl)}; +remove_trailing_align({cons,H,{cons,align,nil}}) -> + H; +remove_trailing_align({seq,H,T}) -> + {seq,H,remove_trailing_align(T)}; +remove_trailing_align(Imm) -> Imm. + +bit_size_propagate(Bin, Type, St) -> + case t_range(Type) of + any -> + St; + {Lb,Ub} -> + set_type(Bin, t_bitstring({Lb,Ub}), St) + end. + +byte_size_propagate(Bin, Type, St) -> + case t_range(Type) of + any -> + St; + {Lb,Ub} -> + set_type(Bin, t_binary({Lb*8,Ub*8}), St) + end. + +char_propagate(Dst, T, NumBits, St) -> + case t_range(T) of + any -> + St; + {Sz,Sz} when Sz*NumBits rem 8 =:= 0 -> + Bits = Sz*NumBits, + set_type(Dst, t_binary({Bits,Bits}), St); + {Lb,Ub} -> + Range = {Lb*NumBits,Ub*NumBits}, + case NumBits rem 8 of + 0 -> + set_type(Dst, t_binary(Range), St); + _ -> + set_type(Dst, t_bitstring(Range), St) + end + end. + +length_propagate(List, Type, St) -> + set_type(List, t_list(t_range(Type)), St). + +enc_opt_cond_1([{Cond,{error,_}=Imm,_}|T], St, Acc) -> + enc_opt_cond_1(T, St, [{Cond,Imm}|Acc]); +enc_opt_cond_1([{Cond,Imm,Curr0}|T], Curr1, Acc) -> + Curr = t_join(Curr0, Curr1), + enc_opt_cond_1(T, Curr, [{Cond,Imm}|Acc]); +enc_opt_cond_1([], St, Acc) -> + {lists:reverse(Acc),St}. + +enc_opt_cs([{Cond,Imm0}|T], St0) -> + case eo_eval_cond(Cond, St0) of + false -> + enc_opt_cs(T, St0); + true -> + {Imm,#ost{t=Type}} = enc_opt(Imm0, St0), + [{'_',Imm,Type}]; + maybe -> + St = update_type_info(Cond, St0), + {Imm,#ost{t=Type}} = enc_opt(Imm0, St), + [{Cond,Imm,Type}|enc_opt_cs(T, St0)] + end; +enc_opt_cs([], _) -> []. + +eo_eval_cond('_', _) -> + true; +eo_eval_cond({Op,{var,_}=Var,Val}, St) -> + Type = get_type(Var, St), + case t_range(Type) of + any -> maybe; + {_,_}=Range -> eval_cond_range(Op, Range, Val) + end; +eo_eval_cond({_Op,{expr,_},_Val}, _St) -> maybe. + +eval_cond_range(lt, {Lb,Ub}, Val) -> + if + Ub < Val -> true; + Val =< Lb -> false; + true -> maybe + end; +eval_cond_range(_Op, _Range, _Val) -> maybe. + +update_type_info({ult,{var,_}=Var,Val}, St) -> + Int = t_integer({0,Val-1}), + Type = t_meet(get_type(Var, St), Int), + set_type(Var, Type, St); +update_type_info({lt,{var,_}=Var,Val}, St) -> + Int = t_integer({0,Val-1}), + Type = t_meet(get_type(Var, St), Int), + set_type(Var, Type, St); +update_type_info({eq,{var,_}=Var,Val}, St) when is_integer(Val) -> + Int = t_integer(Val), + Type = t_meet(get_type(Var, St), Int), + set_type(Var, Type, St); +update_type_info({eq,_,_}, St) -> + St; +update_type_info({ge,_,_}, St) -> St. + +subst_list(As, St) -> + [subst(A, St) || A <- As]. + +subst({var,_}=Var, St) -> + Type = get_type(Var, St), + case t_type(Type) of + integer -> + case t_range(Type) of + any -> Var; + {Val,Val} -> Val; + {_,_} -> Var + end; + _ -> + Var + end; +subst(V, _St) -> V. + +set_type({var,Var}, {_,_}=Type, #ost{sym=Sym0}=St0) -> + Sym1 = gb_trees:enter(Var, Type, Sym0), + case gb_trees:lookup({propagate,Var}, Sym1) of + none -> + St0#ost{sym=Sym1}; + {value,Propagate} -> + Sym = gb_trees:delete({propagate,Var}, Sym1), + St = St0#ost{sym=Sym}, + Propagate(Type, St) + end. + +get_type({var,V}, #ost{sym=Sym}) -> + case gb_trees:lookup(V, Sym) of + none -> t_any(); + {value,T} -> T + end. + +propagate({var,Var}, Propagate, #ost{sym=Sym0}=St) when is_function(Propagate, 2) -> + Sym = gb_trees:enter({propagate,Var}, Propagate, Sym0), + St#ost{sym=Sym}. + +%%% +%%% A simple type system. +%%% +%%% Each type descriptions is a tuple {Type,Range}. +%%% Type is one of the following atoms: +%%% +%%% Type name Description +%%% --------- ----------- +%%% any Anything. +%%% +%%% align Basically iodata, but the list may contain bitstrings +%%% and the the atom 'align'. Can be passed to complete/1 +%%% to construct a binary. Only used for aligned PER (per). +%%% +%%% bitstring An Erlang bitstring. +%%% +%%% bitlist A list that may be passed to list_to_bitstring/1 to +%%% construct a bitstring. +%%% NOTE: When analysing aligned PER (per), the number +%%% of bits in the bitlist is always divisible by 8 (if +%%% not, the type will be 'align' instead). +%%% +%%% binary An Erlang binary (the number of bits is divisible by 8). +%%% +%%% iolist An Erlang iolist. +%%% +%%% nil [] +%%% +%%% integer An integer. +%%% +%%% +%%% Range is one of: +%%% +%%% any +%%% {LowerBound,UpperBound} +%%% +%%% + +t_align(Range) -> + {align,t__range(Range)}. + +t_any() -> + {any,any}. + +t_binary() -> + {binary,any}. + +t_binary(Range) -> + {binary,t__range(Range)}. + +t_bitlist() -> + {bitlist,any}. + +t_bitstring() -> + {bitstring,any}. + +t_bitstring(Range0) -> + case t__range(Range0) of + {Bits,Bits}=Range when Bits rem 8 =:= 0 -> + {binary,Range}; + Range -> + {bitstring,Range} + end. + +t_add({integer,{Lb,Ub}}, N) -> + {integer,{Lb+N,Ub+N}}. + +t_cons({_,_}=T1, {_,_}=T2) -> + T = case {t__cons_type(T1),t__cons_type(T2)} of + {_,any} -> any; + {any,_} -> any; + {align,_} -> align; + {_,align} -> align; + {binary,binary} -> iolist; + {binary,bitstring} -> bitlist; + {bitstring,binary} -> bitlist; + {bitstring,bitstring} -> bitlist + end, + {T,t__cons_ranges(t__cons_range(T1), t__cons_range(T2))}. + +t_integer() -> + {integer,any}. + +t_integer(Range) -> + {integer,t__range(Range)}. + +t_iolist() -> + {iolist,any}. + +t_list(Range) -> + {list,t__range(Range)}. + +t_nil() -> + {nil,{0,0}}. + +t_meet({T1,Range1}, {T2,Range2}) -> + {t_meet_types(T1, T2),t_meet_ranges(Range1, Range2)}. + +t_meet_types(integer, integer) -> integer; +t_meet_types(any, integer) -> integer. + +t_meet_ranges(any, Range) -> + Range; +t_meet_ranges({Lb1,Ub1}, {Lb2,Ub2}) -> + if + Lb1 =< Ub2, Lb2 =< Ub1 -> + {max(Lb1, Lb2),Ub1}; + Lb2 =< Ub1, Lb1 =< Ub2 -> + {max(Lb1, Lb2),Ub2} + end. + +t_join({T1,Range1}, {T2,Range2}) -> + T = t_join_types(lists:sort([T1,T2])), + Range = t_join_ranges(Range1, Range2), + {T,Range}. + +t_join_ranges({Lb1,Ub1}, {Lb2,Ub2}) -> + {min(Lb1, Lb2),max(Ub1, Ub2)}; +t_join_ranges(any, _) -> any; +t_join_ranges(_, any) -> any. + +t_join_types([T,T]) -> T; +t_join_types([align,any]) -> any; +t_join_types([align,_]) -> align; +t_join_types([any,_]) -> any; +t_join_types([bitlist,bitstring]) -> any; +t_join_types([bitlist,integer]) -> any; +t_join_types([bitlist,iolist]) -> bitlist; +t_join_types([bitlist,nil]) -> bitlist; +t_join_types([binary,bitlist]) -> bitlist; +t_join_types([binary,bitstring]) -> bitstring; +t_join_types([binary,integer]) -> binary; +t_join_types([binary,iolist]) -> iolist; +t_join_types([binary,nil]) -> iolist; +t_join_types([bitstring,integer]) -> any; +t_join_types([bitstring,iolist]) -> any; +t_join_types([bitstring,nil]) -> any; +t_join_types([integer,_]) -> any; +t_join_types([iolist,nil]) -> iolist. + +t_type({T,_}) -> T. + +t_range({_,Range}) -> Range. + +t__cons_type({align,_}) -> align; +t__cons_type({any,_}) -> any; +t__cons_type({binary,_}) -> binary; +t__cons_type({bitstring,_}) -> bitstring; +t__cons_type({bitlist,_}) -> bitstring; +t__cons_type({integer,_}) -> binary; +t__cons_type({iolist,_}) -> binary; +t__cons_type({nil,_}) -> binary. + +t__cons_range({integer,_}) -> {8,8}; +t__cons_range({_,Range}) -> Range. + +t__cons_ranges({Lb1,Ub1}, {Lb2,Ub2}) -> + {Lb1+Lb2,Ub1+Ub2}; +t__cons_ranges(any, _) -> any; +t__cons_ranges(_, any) -> any. + +t__range({Lb,Ub}=Range) when is_integer(Lb), is_integer(Ub) -> + Range; +t__range(any) -> + any; +t__range(Val) when is_integer(Val) -> + {Val,Val}. + %%% %%% Code generation for encoding. @@ -1702,19 +2281,10 @@ enc_cg(align) -> enc_cg({apply,F0,As0}) -> As = enc_call_args(As0, ""), case F0 of - {M,F} -> - emit([{asis,M},":",{asis,F},"(",As,")"]); - F when is_atom(F) -> - emit([{asis,F},"(",As,")"]) - end; -enc_cg({apply,F0,As0,Dst}) -> - As = enc_call_args(As0, ""), - emit([mk_val(Dst)," = "]), - case F0 of - {M,F} -> - emit([{asis,M},":",{asis,F},"(",As,")"]); - F when is_atom(F) -> - emit([{asis,F},"(",As,")"]) + {local,F,_} when is_atom(F) -> + emit([{asis,F},"(",As,")"]); + {M,F,_} -> + emit([{asis,M},":",{asis,F},"(",As,")"]) end; enc_cg({assign,Dst0,Expr}) -> Dst = mk_val(Dst0), @@ -1728,15 +2298,11 @@ enc_cg({call,M,F,As0,Dst}) -> As = [mk_val(A) || A <- As0], emit([mk_val(Dst)," = "]), asn1ct_func:call(M, F, As); -enc_cg({call_gen,Prefix,Key,Gen,As0}) -> +enc_cg({call_gen,Prefix,Key,Gen,_,As0}) -> As = [mk_val(A) || A <- As0], asn1ct_func:call_gen(Prefix, Key, Gen, As); enc_cg({'cond',Cs}) -> enc_cg_cond(Cs); -enc_cg({'cond',Cs,Dst0}) -> - Dst = mk_val(Dst0), - emit([Dst," = "]), - enc_cg_cond(Cs); enc_cg({error,Error}) when is_function(Error, 0) -> Error(); enc_cg({error,Var0}) -> @@ -1752,12 +2318,17 @@ enc_cg({lc,Body,Var,List,Dst}) -> emit([mk_val(Dst)," = ["]), enc_cg(Body), emit([" || ",mk_val(Var)," <- ",mk_val(List),"]"]); +enc_cg({list,List,Dst}) -> + emit([mk_val(Dst)," = "]), + enc_cg(List); enc_cg(nil) -> emit("[]"); enc_cg({sub,Src0,Int,Dst0}) -> Src = mk_val(Src0), Dst = mk_val(Dst0), emit([Dst," = ",Src," - ",Int]); +enc_cg({set,{var,Src},{var,Dst}}) -> + emit([Dst," = ",Src]); enc_cg({'try',Try,{P,Succ},Else,Dst}) -> emit([mk_val(Dst)," = try "]), enc_cg(Try), @@ -1792,8 +2363,6 @@ enc_call_args([A|As], Sep) -> [Sep,mk_val(A)|enc_call_args(As, ", ")]; enc_call_args([], _) -> []. -enc_cg_cond([{'_',Action}]) -> - enc_cg(Action); enc_cg_cond(Cs) -> emit("if "), enc_cg_cond(Cs, ""), @@ -1849,7 +2418,7 @@ mk_val(Other) -> {asis,Other}. bit_string_name2pos_fun(NNL, Src) -> {call_gen,"bit_string_name2pos_",NNL, - fun(Fd, Name) -> gen_name2pos(Fd, Name, NNL) end,[Src]}. + fun(Fd, Name) -> gen_name2pos(Fd, Name, NNL) end,[],[Src]}. gen_name2pos(Fd, Name, Names) -> Cs0 = gen_name2pos_cs(Names, Name), @@ -1978,19 +2547,12 @@ enc_opt_al(Imm0) -> {Imm,_} = enc_opt_al_1(Imm0, unknown), Imm. -enc_opt_al_1([{'cond',Cs0,Dst},{call,per,complete,[Dst],Bin}|T0], Al0) -> - {Cs1,{M,F}} = enc_opt_al_prepare_cond(Cs0), - {Cs,_} = enc_opt_al_cond(Cs1, 0), - {T,Al} = enc_opt_al_1([{call,M,F,[Dst],Bin}|T0], Al0), - {[{'cond',Cs,Dst}|T],Al}; enc_opt_al_1([H0|T0], Al0) -> {H,Al1} = enc_opt_al(H0, Al0), {T,Al} = enc_opt_al_1(T0, Al1), {H++T,Al}; enc_opt_al_1([], Al) -> {[],Al}. -enc_opt_al({apply,_,_,_}=Imm, Al) -> - {[Imm],Al}; enc_opt_al({assign,_,_}=Imm, Al) -> {[Imm],Al}; enc_opt_al({block,Bl0}, Al0) -> @@ -2012,6 +2574,10 @@ enc_opt_al({'cond',Cs0}, Al0) -> {[{'cond',Cs}],Al}; enc_opt_al({error,_}=Imm, Al) -> {[Imm],Al}; +enc_opt_al({list,Imm0,Dst}, Al) -> + Imm1 = enc_opt_hoist_align(Imm0), + {Imm,_} = enc_opt_al_1(Imm1, 0), + {[{list,Imm,Dst}],Al}; enc_opt_al({put_bits,V,N,[U,align]}, Al0) when Al0 rem 8 =:= 0 -> Al = if is_integer(N) -> N*U; @@ -2038,8 +2604,12 @@ enc_opt_al({put_bits,_,N,[U]}=PutBits, Al) when is_integer(N), is_integer(Al) -> {[PutBits],Al+N*U}; enc_opt_al({put_bits,_,binary,[U]}=PutBits, Al) when U rem 8 =:= 0 -> {[PutBits],Al}; +enc_opt_al({set,_,_}=Imm, Al) -> + {[Imm],Al}; enc_opt_al({sub,_,_,_}=Imm, Al) -> {[Imm],Al}; +enc_opt_al({'try',_,_,_,_}=Imm, Al) -> + {[Imm],Al}; enc_opt_al(Imm, _) -> {[Imm],unknown}. @@ -2063,29 +2633,25 @@ enc_opt_al_cond_1([], _, CAcc, AAcc) -> end, {lists:reverse(CAcc),Al}. -enc_opt_al_prepare_cond(Cs0) -> - try enc_opt_al_prepare_cond_1(Cs0) of - Cs -> - {Cs,{erlang,iolist_to_binary}} +enc_opt_hoist_align([{'cond',Cs0},{put_bits,0,0,[1,align]}]=Imm) -> + try + Cs = [insert_align_last(C) || C <- Cs0], + [{'cond',Cs}] catch throw:impossible -> - {Cs0,{per,complete}} - end. - -enc_opt_al_prepare_cond_1(Cs) -> - [[C|enc_opt_al_prepare_cond_2(Act)] || [C|Act] <- Cs]. - -enc_opt_al_prepare_cond_2([{put_bits,_,binary,[U|_]}|_]) when U rem 8 =/= 0 -> - throw(impossible); -enc_opt_al_prepare_cond_2([{put_bits,_,_,_}=H|T]) -> - [H|enc_opt_al_prepare_cond_2(T)]; -enc_opt_al_prepare_cond_2([{call,per_common,encode_fragmented,_}=H|T]) -> - [H|enc_opt_al_prepare_cond_2(T)]; -enc_opt_al_prepare_cond_2([_|_]) -> - throw(impossible); -enc_opt_al_prepare_cond_2([]) -> - [{put_bits,0,0,[1,align]}]. + Imm + end; +enc_opt_hoist_align(Imm) -> Imm. +insert_align_last([_,{error,_}]=C) -> + C; +insert_align_last([H|T]) -> + case lists:last(T) of + {put_bits,_,_,_} -> + [H|T ++ [{put_bits,0,0,[1,align]}]]; + _ -> + throw(impossible) + end. %%% %%% For the aligned PER format, fix up the intermediate format @@ -2095,8 +2661,6 @@ enc_opt_al_prepare_cond_2([]) -> per_fixup([{apply,_,_}=H|T]) -> [H|per_fixup(T)]; -per_fixup([{apply,_,_,_}=H|T]) -> - [H|per_fixup(T)]; per_fixup([{block,Block}|T]) -> [{block,per_fixup(Block)}|per_fixup(T)]; per_fixup([{'assign',_,_}=H|T]) -> @@ -2104,14 +2668,11 @@ per_fixup([{'assign',_,_}=H|T]) -> per_fixup([{'cond',Cs0}|T]) -> Cs = [[C|per_fixup(Act)] || [C|Act] <- Cs0], [{'cond',Cs}|per_fixup(T)]; -per_fixup([{'cond',Cs0,Dst}|T]) -> - Cs = [[C|per_fixup(Act)] || [C|Act] <- Cs0], - [{'cond',Cs,Dst}|per_fixup(T)]; per_fixup([{call,_,_,_}=H|T]) -> [H|per_fixup(T)]; per_fixup([{call,_,_,_,_}=H|T]) -> [H|per_fixup(T)]; -per_fixup([{call_gen,_,_,_,_}=H|T]) -> +per_fixup([{call_gen,_,_,_,_,_}=H|T]) -> [H|per_fixup(T)]; per_fixup([{error,_}=H|T]) -> [H|per_fixup(T)]; @@ -2119,6 +2680,10 @@ per_fixup([{lc,B,V,L}|T]) -> [{lc,per_fixup(B),V,L}|per_fixup(T)]; per_fixup([{lc,B,V,L,Dst}|T]) -> [{lc,per_fixup(B),V,L,Dst}|per_fixup(T)]; +per_fixup([{list,Imm,Dst}|T]) -> + [{list,per_fixup(Imm),Dst}|per_fixup(T)]; +per_fixup([{set,_,_}=H|T]) -> + [H|per_fixup(T)]; per_fixup([{sub,_,_,_}=H|T]) -> [H|per_fixup(T)]; per_fixup([{'try',Try0,{P,Succ0},Else0,Dst}|T]) -> diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl index 283616b157..3891fce8d3 100644 --- a/lib/asn1/src/asn1ct_parser2.erl +++ b/lib/asn1/src/asn1ct_parser2.erl @@ -25,7 +25,8 @@ %% Only used internally within this module. -record(typereference, {pos,val}). --record(constraint,{c,e}). +-record(constraint, {c,e}). +-record(identifier, {pos,val}). %% parse all types in module parse(Tokens) -> @@ -112,6 +113,9 @@ parse_ModuleDefinition(Tokens) -> parse_Exports([{'EXPORTS',_L1},{';',_L2}|Rest]) -> {{exports,[]},Rest}; +parse_Exports([{'EXPORTS',_},{'ALL',_},{';',_}|Rest]) -> + %% Same as no exports definition. + {{exports,all},Rest}; parse_Exports([{'EXPORTS',_L1}|Rest]) -> {SymbolList,Rest2} = parse_SymbolList(Rest), case Rest2 of @@ -1037,10 +1041,6 @@ parse_DefinedObjectClass([{typereference,_,_ModName},{'.',_},Tr={typereference,_ parse_DefinedObjectClass([Tr={typereference,_,_ObjClName}|Rest]) -> % {{objectclassname,tref2Exttref(Tr)},Rest}; {tref2Exttref(Tr),Rest}; -parse_DefinedObjectClass([{'TYPE-IDENTIFIER',_}|Rest]) -> - {'TYPE-IDENTIFIER',Rest}; -parse_DefinedObjectClass([{'ABSTRACT-SYNTAX',_}|Rest]) -> - {'ABSTRACT-SYNTAX',Rest}; parse_DefinedObjectClass(Tokens) -> throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module), [got,get_token(hd(Tokens)),expected, @@ -1051,7 +1051,8 @@ parse_DefinedObjectClass(Tokens) -> parse_ObjectClassAssignment([{typereference,L1,ObjClName},{'::=',_}|Rest]) -> {Type,Rest2} = parse_ObjectClass(Rest), - {#classdef{pos=L1,name=ObjClName,typespec=Type},Rest2}; + {#classdef{pos=L1,name=ObjClName,module=resolve_module(Type), + typespec=Type},Rest2}; parse_ObjectClassAssignment(Tokens) -> throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module), [got,get_token(hd(Tokens)),expected, @@ -2134,8 +2135,7 @@ parse_ParameterizedObjectSetAssignment(Tokens) -> %% Parameter = {Governor,Reference} | Reference %% Governor = Type | DefinedObjectClass %% Type = #type{} -%% DefinedObjectClass = #'Externaltypereference'{} | -%% 'ABSTRACT-SYNTAX' | 'TYPE-IDENTIFIER' +%% DefinedObjectClass = #'Externaltypereference'{} %% Reference = #'Externaltypereference'{} | #'Externalvaluereference'{} parse_ParameterList([{'{',_}|Rest]) -> parse_ParameterList(Rest,[]); @@ -2863,13 +2863,14 @@ parse_SequenceValue(Tokens) -> throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module), [got,get_token(hd(Tokens)),expected,'{']}}). -parse_SequenceValue([{identifier,_,IdName}|Rest],Acc) -> +parse_SequenceValue([{identifier,Pos,IdName}|Rest],Acc) -> {Value,Rest2} = parse_Value(Rest), + SeqTag = #seqtag{pos=Pos,module=get(asn1_module),val=IdName}, case Rest2 of [{',',_}|Rest3] -> - parse_SequenceValue(Rest3,[{IdName,Value}|Acc]); + parse_SequenceValue(Rest3, [{SeqTag,Value}|Acc]); [{'}',_}|Rest3] -> - {lists:reverse([{IdName,Value}|Acc]),Rest3}; + {lists:reverse(Acc, [{SeqTag,Value}]),Rest3}; _ -> throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module), [got,get_token(hd(Rest2)),expected,'}']}}) diff --git a/lib/asn1/src/asn1ct_table.erl b/lib/asn1/src/asn1ct_table.erl index a5eb6d0413..2eca80eda3 100644 --- a/lib/asn1/src/asn1ct_table.erl +++ b/lib/asn1/src/asn1ct_table.erl @@ -22,34 +22,25 @@ %% Table abstraction module for ASN.1 compiler -export([new/1]). --export([new/2]). -export([new_reuse/1]). --export([new_reuse/2]). -export([exists/1]). -export([size/1]). -export([insert/2]). -export([lookup/2]). -export([match/2]). -export([to_list/1]). --export([delete/1]). % TODO: Remove (since we run in a separate process) +-export([delete/1]). -%% Always creates a new table -new(Table) -> new(Table, []). -new(Table, Options) -> - TableId = case get(Table) of - undefined -> - ets:new(Table, Options); - _ -> - delete(Table), - ets:new(Table, Options) - end, +%% Always create a new table. +new(Table) -> + undefined = get(Table), %Assertion. + TableId = ets:new(Table, []), put(Table, TableId). -%% Only create it if it doesn't exist yet -new_reuse(Table) -> new_reuse(Table, []). -new_reuse(Table, Options) -> - not exists(Table) andalso new(Table, Options). +%% Only create it if it doesn't exist yet. +new_reuse(Table) -> + not exists(Table) andalso new(Table). exists(Table) -> get(Table) =/= undefined. @@ -63,14 +54,17 @@ match(Table, MatchSpec) -> ets:match(get(Table), MatchSpec). to_list(Table) -> ets:tab2list(get(Table)). +%% Deleting tables is no longer strictly necessary since each compilation +%% runs in separate process, but it will reduce memory consumption +%% especially when many compilations are run in parallel. + delete(Tables) when is_list(Tables) -> [delete(T) || T <- Tables], true; delete(Table) when is_atom(Table) -> - case get(Table) of + case erase(Table) of undefined -> true; TableId -> - ets:delete(TableId), - erase(Table) + ets:delete(TableId) end. diff --git a/lib/asn1/src/asn1ct_tok.erl b/lib/asn1/src/asn1ct_tok.erl index 85199c65ec..8687ed955c 100644 --- a/lib/asn1/src/asn1ct_tok.erl +++ b/lib/asn1/src/asn1ct_tok.erl @@ -36,7 +36,7 @@ process(Stream,Lno,R) -> process(io:get_line(Stream, ''), Stream,Lno+1,R). process(eof, Stream,Lno,R) -> - file:close(Stream), + ok = file:close(Stream), lists:flatten(lists:reverse([{'$end',Lno}|R])); @@ -309,7 +309,6 @@ check_hex(_) -> %% returns rstrtype if A is a reserved word in the group %% RestrictedCharacterStringType reserved_word('ABSENT') -> true; -%reserved_word('ABSTRACT-SYNTAX') -> true; % impl as predef item reserved_word('ALL') -> true; reserved_word('ANY') -> true; reserved_word('APPLICATION') -> true; @@ -380,7 +379,6 @@ reserved_word('T61String') -> rstrtype; reserved_word('TAGS') -> true; reserved_word('TeletexString') -> rstrtype; reserved_word('TRUE') -> true; -%% reserved_word('TYPE-IDENTIFIER') -> true; % impl as predef item reserved_word('UNION') -> true; reserved_word('UNIQUE') -> true; reserved_word('UNIVERSAL') -> true; diff --git a/lib/asn1/src/asn1ct_value.erl b/lib/asn1/src/asn1ct_value.erl index 862b3c4ea5..221cd991a7 100644 --- a/lib/asn1/src/asn1ct_value.erl +++ b/lib/asn1/src/asn1ct_value.erl @@ -18,6 +18,7 @@ %% %% -module(asn1ct_value). +-compile([{nowarn_deprecated_function,{asn1rt,utf8_list_to_binary,1}}]). %% Generate Erlang values for ASN.1 types. %% The value is randomized within it's constraints @@ -260,7 +261,11 @@ from_type_prim(M, D) -> 'BOOLEAN' -> true; 'OCTET STRING' -> - adjust_list(size_random(C),c_string(C,"OCTET STRING")); + S0 = adjust_list(size_random(C), c_string(C, "OCTET STRING")), + case M:legacy_erlang_types() of + false -> list_to_binary(S0); + true -> S0 + end; 'NumericString' -> adjust_list(size_random(C),c_string(C,"0123456789")); 'TeletexString' -> @@ -348,7 +353,7 @@ random_unnamed_bit_string(M, C) -> random(Upper) -> {A1,A2,A3} = erlang:now(), - random:seed(A1,A2,A3), + _ = random:seed(A1, A2, A3), random:uniform(Upper). size_random(C) -> diff --git a/lib/asn1/src/asn1rt.erl b/lib/asn1/src/asn1rt.erl index d18f81346a..ad8b879c38 100644 --- a/lib/asn1/src/asn1rt.erl +++ b/lib/asn1/src/asn1rt.erl @@ -18,14 +18,13 @@ %% %% -module(asn1rt). +-deprecated(module). %% Runtime functions for ASN.1 (i.e encode, decode) -export([encode/2,encode/3,decode/3,load_driver/0,unload_driver/0,info/1]). -export([utf8_binary_to_list/1,utf8_list_to_binary/1]). - --deprecated([load_driver/0,unload_driver/0]). encode(Module,{Type,Term}) -> encode(Module,Type,Term). diff --git a/lib/asn1/src/asn1rt_nif.erl b/lib/asn1/src/asn1rt_nif.erl index c1879e3dcf..1a44f1a27c 100644 --- a/lib/asn1/src/asn1rt_nif.erl +++ b/lib/asn1/src/asn1rt_nif.erl @@ -30,7 +30,7 @@ -define(ASN1_NIF_VSN,1). load_nif() -> - LibBaseName = "asn1_erl_nif", + LibBaseName = "asn1rt_nif", PrivDir = code:priv_dir(asn1), LibName = case erlang:system_info(build_type) of opt -> diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl index 583ff790b7..c4cd872368 100644 --- a/lib/asn1/src/asn1rtt_ber.erl +++ b/lib/asn1/src/asn1rtt_ber.erl @@ -26,22 +26,24 @@ skip_ExtensionAdditions/2]). -export([encode_boolean/2,decode_boolean/2, encode_integer/2,encode_integer/3, - decode_integer/2,decode_integer/3, - decode_named_integer/3,decode_named_integer/4, - encode_enumerated/2,decode_enumerated/3, + decode_integer/2, + number2name/2, + encode_unnamed_bit_string/2,encode_unnamed_bit_string/3, + encode_named_bit_string/3,encode_named_bit_string/4, encode_bit_string/4, decode_named_bit_string/3, - decode_compact_bit_string/3, - decode_legacy_bit_string/3, - decode_native_bit_string/3, + decode_compact_bit_string/2,compact_bit_string_size/1, + decode_native_bit_string/2, + native_to_legacy_bit_string/1, encode_null/2,decode_null/2, encode_relative_oid/2,decode_relative_oid/2, encode_object_identifier/2,decode_object_identifier/2, encode_restricted_string/2, - decode_restricted_string/2,decode_restricted_string/3, - encode_universal_string/2,decode_universal_string/3, + decode_octet_string/2, + decode_restricted_string/2, + encode_universal_string/2,decode_universal_string/2, encode_UTF8_string/2,decode_UTF8_string/2, - encode_BMP_string/2,decode_BMP_string/3]). + encode_BMP_string/2,decode_BMP_string/2]). -export([encode_open_type/2,decode_open_type/2, decode_open_type_as_binary/2]). @@ -588,8 +590,6 @@ encode_tags(TagIn, {BytesSoFar,LenSoFar}) -> encode_open_type(Val, T) when is_list(Val) -> encode_open_type(list_to_binary(Val), T); -encode_open_type(Val, []) -> - {Val,byte_size(Val)}; encode_open_type(Val, Tag) -> encode_tags(Tag, Val, byte_size(Val)). @@ -694,41 +694,14 @@ encode_integer_neg(N, Acc) -> %%=============================================================================== %% decode integer -%% (Buffer, Range, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes} -%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes} %%=============================================================================== -decode_named_integer(Tlv, NamedNumberList, TagIn) -> - V = match_tags(Tlv, TagIn), - Int = decode_integer(V), - number2name(Int, NamedNumberList). - -decode_named_integer(Tlv, Range, NamedNumberList, TagIn) -> - V = match_tags(Tlv, TagIn), - Int = range_check_integer(decode_integer(V), Range), - number2name(Int, NamedNumberList). - decode_integer(Tlv, TagIn) -> - V = match_tags(Tlv, TagIn), - decode_integer(V). - -decode_integer(Tlv, Range, TagIn) -> - V = match_tags(Tlv, TagIn), - Int = decode_integer(V), - range_check_integer(Int, Range). - -decode_integer(Bin) -> + Bin = match_tags(Tlv, TagIn), Len = byte_size(Bin), <<Int:Len/signed-unit:8>> = Bin, Int. -range_check_integer(Int, {Lb,Ub}) when Lb =< Int, Int =< Ub -> - Int; -range_check_integer(Int, Range) -> - exit({error,{asn1,{integer_range,Range,Int}}}). - -number2name(Int, []) -> - Int; number2name(Int, NamedNumberList) -> case lists:keyfind(Int, 2, NamedNumberList) of {NamedVal,_} -> @@ -737,49 +710,56 @@ number2name(Int, NamedNumberList) -> Int end. - %%============================================================================ -%% Enumerated value, ITU_T X.690 Chapter 8.4 - -%% encode enumerated value +%% Bitstring value, ITU_T X.690 Chapter 8.6 +%% +%% encode bitstring value %%============================================================================ -encode_enumerated(Val, TagIn) when is_integer(Val) -> - encode_tags(TagIn, encode_integer(Val)). -%%============================================================================ -%% decode enumerated value -%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> Value -%%=========================================================================== -decode_enumerated(Tlv, NamedNumberList, Tags) -> - Buffer = match_tags(Tlv, Tags), - decode_enumerated_notag(Buffer, NamedNumberList, Tags). - -decode_enumerated_notag(Buffer, {NamedNumberList,ExtList}, _Tags) -> - IVal = decode_integer(Buffer), - case decode_enumerated1(IVal, NamedNumberList) of - {asn1_enum,IVal} -> - decode_enumerated1(IVal,ExtList); - EVal -> - EVal - end; -decode_enumerated_notag(Buffer, NNList, _Tags) -> - IVal = decode_integer(Buffer), - case decode_enumerated1(IVal, NNList) of - {asn1_enum,_} -> - exit({error,{asn1, {illegal_enumerated, IVal}}}); - EVal -> - EVal - end. +encode_unnamed_bit_string(Bits, TagIn) -> + Unused = (8 - (bit_size(Bits) band 7)) band 7, + Bin = <<Unused,Bits/bitstring,0:Unused>>, + encode_tags(TagIn, Bin, byte_size(Bin)). -decode_enumerated1(Val, NamedNumberList) -> - %% it must be a named integer - case lists:keyfind(Val, 2, NamedNumberList) of - {NamedVal, _} -> - NamedVal; - _ -> - {asn1_enum,Val} +encode_unnamed_bit_string(MaxBits, Bits, TagIn) -> + NumBits = bit_size(Bits), + Unused = (8 - (NumBits band 7)) band 7, + Bin = <<Unused,Bits/bitstring,0:Unused>>, + if + NumBits > MaxBits -> + exit({error,{asn1, + {bitstring_length, + {{was,NumBits},{maximum,MaxBits}}}}}); + true -> + encode_tags(TagIn, Bin, byte_size(Bin)) end. +encode_named_bit_string([H|_]=Bits, NamedBitList, TagIn) when is_atom(H) -> + do_encode_named_bit_string(Bits, NamedBitList, TagIn); +encode_named_bit_string([{bit,_}|_]=Bits, NamedBitList, TagIn) -> + do_encode_named_bit_string(Bits, NamedBitList, TagIn); +encode_named_bit_string(Bits, _NamedBitList, TagIn) when is_bitstring(Bits) -> + encode_unnamed_bit_string(Bits, TagIn). + +encode_named_bit_string(C, [H|_]=Bits, NamedBitList, TagIn) when is_atom(H) -> + do_encode_named_bit_string(C, Bits, NamedBitList, TagIn); +encode_named_bit_string(C, [{bit,_}|_]=Bits, NamedBitList, TagIn) -> + do_encode_named_bit_string(C, Bits, NamedBitList, TagIn); +encode_named_bit_string(C, Bits, _NamedBitList, TagIn) when is_bitstring(Bits) -> + encode_unnamed_bit_string(C, Bits, TagIn). + +do_encode_named_bit_string([FirstVal | RestVal], NamedBitList, TagIn) -> + ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []), + Size = lists:max(ToSetPos) + 1, + BitList = make_and_set_list(Size, ToSetPos, 0), + {Len,Unused,OctetList} = encode_bitstring(BitList), + encode_tags(TagIn, [Unused|OctetList],Len+1). + +do_encode_named_bit_string(Size, [FirstVal | RestVal], NamedBitList, TagIn) -> + ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []), + BitList = make_and_set_list(Size, ToSetPos, 0), + {Len, Unused, OctetList} = encode_bitstring(BitList), + encode_tags(TagIn, [Unused|OctetList], Len+1). %%============================================================================ %% Bitstring value, ITU_T X.690 Chapter 8.6 @@ -880,15 +860,14 @@ remove_unused_then_dotag(TagIn,Unused,BinBits) -> encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn) -> ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []), - Size = - case C of - [] -> - lists:max(ToSetPos)+1; - {_Min,Max} -> - Max; - TSize -> - TSize - end, + Size = case C of + [] -> + lists:max(ToSetPos) + 1; + {_Min,Max} -> + Max; + TSize -> + TSize + end, BitList = make_and_set_list(Size, ToSetPos, 0), {Len, Unused, OctetList} = encode_bitstring(BitList), encode_tags(TagIn, [Unused|OctetList],Len+1). @@ -1046,33 +1025,23 @@ unused_bitlist([Bit | Rest], Trail, Ack) -> %% decode bitstring value %%============================================================================ -decode_compact_bit_string(Buffer, Range, Tags) -> +decode_compact_bit_string(Buffer, Tags) -> case match_and_collect(Buffer, Tags) of - <<0>> -> - check_restricted_string({0,<<>>}, 0, Range); - <<Unused,Bits/binary>> -> - Val = {Unused,Bits}, - Len = bit_size(Bits) - Unused, - check_restricted_string(Val, Len, Range) + <<0>> -> {0,<<>>}; + <<Unused,Bits/binary>> -> {Unused,Bits} end. -decode_legacy_bit_string(Buffer, Range, Tags) -> - Val = case match_and_collect(Buffer, Tags) of - <<0>> -> - []; - <<Unused,Bits/binary>> -> - decode_bitstring2(byte_size(Bits), Unused, Bits) - end, - check_restricted_string(Val, length(Val), Range). +compact_bit_string_size({Unused,Bits}) -> + bit_size(Bits) - Unused. -decode_native_bit_string(Buffer, Range, Tags) -> +decode_native_bit_string(Buffer, Tags) -> case match_and_collect(Buffer, Tags) of <<0>> -> - check_restricted_string(<<>>, 0, Range); + <<>>; <<Unused,Bits/binary>> -> Size = bit_size(Bits) - Unused, <<Val:Size/bitstring,_:Unused/bitstring>> = Bits, - check_restricted_string(Val, Size, Range) + Val end. decode_named_bit_string(Buffer, NamedNumberList, Tags) -> @@ -1095,6 +1064,9 @@ decode_bitstring2(Len, Unused, [B7,B6,B5,B4,B3,B2,B1,B0| decode_bitstring2(Len - 1, Unused, Buffer)]. +native_to_legacy_bit_string(Bits) -> + [B || <<B:1>> <= Bits]. + %%---------------------------------------- %% Decode the bitlist to names %%---------------------------------------- @@ -1251,25 +1223,19 @@ encode_restricted_string(OctetList, TagIn) when is_list(OctetList) -> encode_tags(TagIn, OctetList, length(OctetList)). %%============================================================================ -%% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings +%% decode OCTET STRING to binary %%============================================================================ -decode_restricted_string(Tlv, TagsIn) -> +decode_octet_string(Tlv, TagsIn) -> Bin = match_and_collect(Tlv, TagsIn), - binary_to_list(Bin). + binary:copy(Bin). -decode_restricted_string(Tlv, Range, TagsIn) -> - Bin = match_and_collect(Tlv, TagsIn), - check_restricted_string(binary_to_list(Bin), byte_size(Bin), Range). +%%============================================================================ +%% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings +%%============================================================================ -check_restricted_string(Val, _Len, []) -> - Val; -check_restricted_string(Val, Len, {Lb,Ub}) when Lb =< Len, Len =< Ub -> - Val; -check_restricted_string(Val, Len, Len) -> - Val; -check_restricted_string(Val, _Len, Range) -> - exit({error,{asn1,{length,Range,Val}}}). +decode_restricted_string(Tlv, TagsIn) -> + match_and_collect(Tlv, TagsIn). %%============================================================================ %% encode Universal string @@ -1295,10 +1261,9 @@ mk_uni_list([H|T],List) -> %% {String, Remain, RemovedBytes} %%=========================================================================== -decode_universal_string(Buffer, Range, Tags) -> +decode_universal_string(Buffer, Tags) -> Bin = match_and_collect(Buffer, Tags), - Val = mk_universal_string(binary_to_list(Bin)), - check_restricted_string(Val, length(Val), Range). + mk_universal_string(binary_to_list(Bin)). mk_universal_string(In) -> mk_universal_string(In, []). @@ -1358,10 +1323,9 @@ mk_BMP_list([H|T], List) -> %% (Buffer, Range, StringType, HasTag, TotalLen) -> %% {String, Remain, RemovedBytes} %%============================================================================ -decode_BMP_string(Buffer, Range, Tags) -> +decode_BMP_string(Buffer, Tags) -> Bin = match_and_collect(Buffer, Tags), - Val = mk_BMP_string(binary_to_list(Bin)), - check_restricted_string(Val, length(Val), Range). + mk_BMP_string(binary_to_list(Bin)). mk_BMP_string(In) -> mk_BMP_string(In,[]). diff --git a/lib/asn1/src/asn1rtt_check.erl b/lib/asn1/src/asn1rtt_check.erl index be4f9c8bff..0083867a10 100644 --- a/lib/asn1/src/asn1rtt_check.erl +++ b/lib/asn1/src/asn1rtt_check.erl @@ -18,43 +18,41 @@ %% -module(asn1rtt_check). --export([check_bool/2, +-export([check_fail/1, check_int/3, - check_bitstring/2,check_named_bitstring/3, + check_legacy_bitstring/2, + check_legacy_named_bitstring/3, + check_legacy_named_bitstring/4, + check_named_bitstring/3, + check_named_bitstring/4, + check_literal_sof/2, check_octetstring/2, - check_null/2, check_objectidentifier/2, check_objectdescriptor/2, check_real/2, - check_enum/3, check_restrictedstring/2]). -check_bool(_Bool, asn1_DEFAULT) -> - true; -check_bool(Bool, Bool) when is_boolean(Bool) -> - true; -check_bool(_Bool1, Bool2) -> - throw({error,Bool2}). +check_fail(_) -> + throw(false). -check_int(_, asn1_DEFAULT, _) -> - true; check_int(Value, Value, _) when is_integer(Value) -> true; -check_int(DefValue, Value, NNL) when is_atom(Value) -> +check_int(Value, DefValue, NNL) when is_atom(Value) -> case lists:keyfind(Value, 1, NNL) of {_,DefValue} -> true; _ -> - throw({error,DefValue}) + throw(false) end; -check_int(DefaultValue, _Value, _) -> - throw({error,DefaultValue}). +check_int(_, _, _) -> + throw(false). + +check_legacy_bitstring(Value, Default) -> + check_bitstring(Default, Value). %% check_bitstring(Default, UserBitstring) -> true|false %% Default = bitstring() %% UserBitstring = integeger() | list(0|1) | {Unused,binary()} | bitstring() -check_bitstring(_, asn1_DEFAULT) -> - true; check_bitstring(DefVal, {Unused,Binary}) -> %% User value in compact format. Sz = bit_size(Binary) - Unused, @@ -62,7 +60,7 @@ check_bitstring(DefVal, {Unused,Binary}) -> check_bitstring(DefVal, Val); check_bitstring(DefVal, Val) when is_bitstring(Val) -> case Val =:= DefVal of - false -> throw(error); + false -> throw(false); true -> true end; check_bitstring(Def, Val) when is_list(Val) -> @@ -75,178 +73,95 @@ check_bitstring_list(<<H:1,T1/bitstring>>, [H|T2]) -> check_bitstring_list(<<>>, []) -> true; check_bitstring_list(_, _) -> - throw(error). + throw(false). check_bitstring_integer(<<H:1,T1/bitstring>>, Int) when H =:= Int band 1 -> check_bitstring_integer(T1, Int bsr 1); check_bitstring_integer(<<>>, 0) -> true; check_bitstring_integer(_, _) -> - throw(error). - -check_named_bitstring(_, asn1_DEFAULT, _) -> - true; -check_named_bitstring(V, V, _) -> - true; -%% Default value and user value as lists of ones and zeros -check_named_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL=[_H|_T]) when is_integer(H1), is_integer(H2) -> - L2new = remove_trailing_zeros(L2), - check_named_bitstring(L1, L2new, NBL); -%% Default value as a list of 1 and 0 and user value as a list of atoms -check_named_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL) when is_integer(H1), is_atom(H2) -> - L3 = bit_list_to_nbl(L1, NBL, 0, []), - check_named_bitstring(L3, L2, NBL); -%% Both default value and user value as a list of atoms -check_named_bitstring(L1=[H1|T1], L2=[H2|_T2], _) - when is_atom(H1), is_atom(H2), length(L1) =:= length(L2) -> - case lists:member(H1, L2) of - true -> - check_bitstring1(T1, L2); - false -> throw({error,L2}) - end; -%% Default value as a list of atoms and user value as a list of 1 and 0 -check_named_bitstring(L1=[H1|_T1], L2=[H2|_T2], NBL) when is_atom(H1), is_integer(H2) -> - L3 = bit_list_to_nbl(L2, NBL, 0, []), - check_named_bitstring(L1, L3, NBL); -%% User value in compact format -check_named_bitstring(DefVal,CBS={_,_}, NBL) -> - NewVal = cbs_to_bit_list(CBS), - check_named_bitstring(DefVal, NewVal, NBL); -%% User value as a binary -check_named_bitstring(DefVal, CBS, NBL) when is_binary(CBS) -> - NewVal = cbs_to_bit_list({0,CBS}), - check_named_bitstring(DefVal, NewVal, NBL); -%% User value as a bitstring -check_named_bitstring(DefVal, CBS, NBL) when is_bitstring(CBS) -> - BitSize = bit_size(CBS), - Unused = 8 - (BitSize band 7), - NewVal = cbs_to_bit_list({Unused,<<CBS:BitSize/bits,0:Unused>>}), - check_named_bitstring(DefVal, NewVal, NBL); -check_named_bitstring(DV, V, _) -> - throw({error,DV,V}). - -int_to_bit_list(0, Acc, 0) -> - Acc; -int_to_bit_list(Int, Acc, Len) when Len > 0 -> - int_to_bit_list(Int bsr 1, [Int band 1|Acc], Len - 1). - -bit_list_to_nbl([0|T], NBL, Pos, Acc) -> - bit_list_to_nbl(T, NBL, Pos+1, Acc); -bit_list_to_nbl([1|T], NBL, Pos, Acc) -> - case lists:keyfind(Pos, 2, NBL) of - {N,_} -> - bit_list_to_nbl(T, NBL, Pos+1, [N|Acc]); + throw(false). + +check_legacy_named_bitstring([Int|_]=Val, Bs, BsSize) when is_integer(Int) -> + check_named_bitstring(<< <<B:1>> || B <- Val >>, Bs, BsSize); +check_legacy_named_bitstring({Unused,Val0}, Bs, BsSize) -> + Sz = bit_size(Val0) - Unused, + <<Val:Sz/bits,_/bits>> = Val0, + check_named_bitstring(Val, Bs, BsSize); +check_legacy_named_bitstring(Val, Bs, BsSize) when is_integer(Val) -> + L = legacy_int_to_bitlist(Val), + check_named_bitstring(<< <<B:1>> || B <- L >>, Bs, BsSize); +check_legacy_named_bitstring(Val, Bs, BsSize) -> + check_named_bitstring(Val, Bs, BsSize). + +check_legacy_named_bitstring([Int|_]=Val, Names, Bs, BsSize) when is_integer(Int) -> + check_named_bitstring(<< <<B:1>> || B <- Val >>, Names, Bs, BsSize); +check_legacy_named_bitstring({Unused,Val0}, Names, Bs, BsSize) -> + Sz = bit_size(Val0) - Unused, + <<Val:Sz/bits,_/bits>> = Val0, + check_named_bitstring(Val, Names, Bs, BsSize); +check_legacy_named_bitstring(Val, Names, Bs, BsSize) when is_integer(Val) -> + L = legacy_int_to_bitlist(Val), + check_named_bitstring(<< <<B:1>> || B <- L >>, Names, Bs, BsSize); +check_legacy_named_bitstring(Val, Names, Bs, BsSize) -> + check_named_bitstring(Val, Names, Bs, BsSize). + +legacy_int_to_bitlist(0) -> + []; +legacy_int_to_bitlist(Int) -> + [Int band 1|legacy_int_to_bitlist(Int bsr 1)]. + +check_named_bitstring(Bs, Bs, _) -> + true; +check_named_bitstring(Val, Bs, BsSize) -> + Rest = bit_size(Val) - BsSize, + case Val of + <<Bs:BsSize/bits,0:Rest>> -> + true; _ -> - throw({error,{no,named,element,at,pos,Pos}}) - end; -bit_list_to_nbl([], _, _, Acc) -> - Acc. - -remove_trailing_zeros(L2) -> - remove_trailing_zeros1(lists:reverse(L2)). -remove_trailing_zeros1(L) -> - lists:reverse(lists:dropwhile(fun(0)->true; - (_) ->false - end, - L)). - -check_bitstring1([H|T], NBL) -> - case lists:member(H, NBL) of - true -> check_bitstring1(T, NBL); - V -> throw({error,V}) - end; -check_bitstring1([], _) -> - true. - -cbs_to_bit_list({Unused, <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Rest/binary>>}) when byte_size(Rest) >= 1 -> - [B7,B6,B5,B4,B3,B2,B1,B0|cbs_to_bit_list({Unused,Rest})]; -cbs_to_bit_list({0,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1>>}) -> - [B7,B6,B5,B4,B3,B2,B1,B0]; -cbs_to_bit_list({Unused,Bin}) when byte_size(Bin) =:= 1 -> - Used = 8-Unused, - <<Int:Used,_:Unused>> = Bin, - int_to_bit_list(Int, [], Used). - + throw(false) + end. -check_octetstring(_, asn1_DEFAULT) -> - true; -check_octetstring(L, L) -> - true; -check_octetstring(L, Int) when is_list(L), is_integer(Int) -> - case integer_to_octetlist(Int) of - L -> true; - V -> throw({error,V}) +check_named_bitstring([_|_]=Val, Names, _, _) -> + case lists:sort(Val) of + Names -> true; + _ -> throw(false) end; -check_octetstring(_, V) -> - throw({error,V}). - -integer_to_octetlist(Int) -> - integer_to_octetlist(Int, []). -integer_to_octetlist(0, Acc) -> - Acc; -integer_to_octetlist(Int, Acc) -> - integer_to_octetlist(Int bsr 8, [(Int band 255)|Acc]). - -check_null(_, asn1_DEFAULT) -> +check_named_bitstring(Bs, _, Bs, _) -> true; -check_null('NULL', 'NULL') -> - true; -check_null(_, V) -> - throw({error,V}). +check_named_bitstring(Val, _, Bs, BsSize) -> + Rest = bit_size(Val) - BsSize, + case Val of + <<Bs:BsSize/bits,0:Rest>> -> + true; + _ -> + throw(false) + end. -check_objectidentifier(_, asn1_DEFAULT) -> - true; -check_objectidentifier(OI, OI) -> +check_octetstring(V, V) -> true; -check_objectidentifier(DOI, OI) when is_tuple(DOI), is_tuple(OI) -> - check_objectidentifier1(tuple_to_list(DOI), tuple_to_list(OI)); -check_objectidentifier(_, OI) -> - throw({error,OI}). - -check_objectidentifier1([V|Rest1], [V|Rest2]) -> - check_objectidentifier1(Rest1, Rest2, V); -check_objectidentifier1([V1|Rest1], [V2|Rest2]) -> - case reserved_objectid(V2, []) of - V1 -> - check_objectidentifier1(Rest1, Rest2, [V1]); - V -> - throw({error,V}) - end. -check_objectidentifier1([V|Rest1], [V|Rest2], Above) -> - check_objectidentifier1(Rest1, Rest2, [V|Above]); -check_objectidentifier1([V1|Rest1], [V2|Rest2], Above) -> - case reserved_objectid(V2, Above) of - V1 -> - check_objectidentifier1(Rest1, Rest2, [V1|Above]); - V -> - throw({error,V}) +check_octetstring(V, Def) when is_list(V) -> + case list_to_binary(V) of + Def -> true; + _ -> throw(false) + end; +check_octetstring(_, _) -> + throw(false). + +check_objectidentifier(Value, {Prefix,Tail}) when is_tuple(Value) -> + check_oid(tuple_to_list(Value), Prefix, Tail); +check_objectidentifier(_, _) -> + throw(false). + +check_oid([H|T], [K|Ks], Tail) -> + case lists:member(H, K) of + false -> throw(false); + true -> check_oid(T, Ks, Tail) end; -check_objectidentifier1([], [], _) -> +check_oid(Tail, [], Tail) -> true; -check_objectidentifier1(_, V, _) -> - throw({error,object,identifier,V}). - -%% ITU-T Rec. X.680 Annex B - D -reserved_objectid('itu-t', []) -> 0; -reserved_objectid('ccitt', []) -> 0; -%% arcs below "itu-t" -reserved_objectid('recommendation', [0]) -> 0; -reserved_objectid('question', [0]) -> 1; -reserved_objectid('administration', [0]) -> 2; -reserved_objectid('network-operator', [0]) -> 3; -reserved_objectid('identified-organization', [0]) -> 4; - -reserved_objectid(iso, []) -> 1; -%% arcs below "iso", note that number 1 is not used -reserved_objectid('standard', [1]) -> 0; -reserved_objectid('member-body', [1]) -> 2; -reserved_objectid('identified-organization', [1]) -> 3; - -reserved_objectid('joint-iso-itu-t', []) -> 2; -reserved_objectid('joint-iso-ccitt', []) -> 2; - -reserved_objectid(_, _) -> false. - +check_oid(_, _, _) -> + throw(false). check_objectdescriptor(_, asn1_DEFAULT) -> true; @@ -262,21 +177,6 @@ check_real(R, R) -> check_real(_, _) -> throw({error,{not_implemented_yet,check_real}}). -check_enum(_, asn1_DEFAULT, _) -> - true; -check_enum(Val, Val, _) -> - true; -check_enum(Int, Atom, Enumerations) when is_integer(Int), is_atom(Atom) -> - case lists:keyfind(Atom, 1, Enumerations) of - {_,Int} -> true; - _ -> throw({error,{enumerated,Int,Atom}}) - end; -check_enum(DefVal, Val, _) -> - throw({error,{enumerated,DefVal,Val}}). - - -check_restrictedstring(_, asn1_DEFAULT) -> - true; check_restrictedstring(Val, Val) -> true; check_restrictedstring([V|Rest1], [V|Rest2]) -> @@ -295,7 +195,15 @@ check_restrictedstring({V1,V2,V3,V4}, [V1,V2,V3,V4]) -> check_restrictedstring([V1,V2,V3,V4], {V1,V2,V3,V4}) -> true; %% character string list -check_restrictedstring(V1, V2) when is_list(V1), is_tuple(V2) -> - check_restrictedstring(V1, tuple_to_list(V2)); -check_restrictedstring(V1, V2) -> - throw({error,{restricted,string,V1,V2}}). +check_restrictedstring(V1, V2) when is_tuple(V1) -> + check_restrictedstring(tuple_to_list(V1), V2); +check_restrictedstring(_, _) -> + throw(false). + +check_literal_sof(Value, Default) -> + case lists:sort(Value) of + Default -> + true; + _ -> + throw(false) + end. diff --git a/lib/asn1/src/asn1rtt_ext.erl b/lib/asn1/src/asn1rtt_ext.erl index 46adb2007d..f3eee1cdd5 100644 --- a/lib/asn1/src/asn1rtt_ext.erl +++ b/lib/asn1/src/asn1rtt_ext.erl @@ -38,7 +38,7 @@ transform_to_EXTERNAL1990([{'context-negotiation',Context_negot}|Rest], Acc) -> transform_to_EXTERNAL1990([asn1_NOVALUE|Rest], Acc) -> transform_to_EXTERNAL1990(Rest, [asn1_NOVALUE|Acc]); transform_to_EXTERNAL1990([Data_val_desc,Data_value], Acc) - when is_list(Data_value)-> + when is_list(Data_value); is_binary(Data_value) -> list_to_tuple(lists:reverse([{'octet-aligned',Data_value}, Data_val_desc|Acc])); transform_to_EXTERNAL1990([Data_val_desc,Data_value], Acc) diff --git a/lib/asn1/src/asn1rtt_per_common.erl b/lib/asn1/src/asn1rtt_per_common.erl index 3309e6a4ca..0290c75a28 100644 --- a/lib/asn1/src/asn1rtt_per_common.erl +++ b/lib/asn1/src/asn1rtt_per_common.erl @@ -30,6 +30,7 @@ decode_big_chars/2, decode_oid/1,decode_relative_oid/1, encode_chars/2,encode_chars/3, + encode_chars_compact_map/3, encode_chars_16bit/1,encode_big_chars/1, encode_fragmented/2, encode_oid/1,encode_relative_oid/1, @@ -37,8 +38,10 @@ bitstring_from_positions/1,bitstring_from_positions/2, to_bitstring/1,to_bitstring/2, to_named_bitstring/1,to_named_bitstring/2, - is_default_bitstring/5, - extension_bitmap/3]). + bs_drop_trailing_zeroes/1,adjust_trailing_zeroes/2, + is_default_bitstring/3,is_default_bitstring/5, + extension_bitmap/3, + open_type_to_binary/1,legacy_open_type_to_binary/1]). -define('16K',16384). @@ -106,6 +109,9 @@ encode_chars(Val, NumBits) -> encode_chars(Val, NumBits, {Lb,Tab}) -> << <<(enc_char(C, Lb, Tab)):NumBits>> || C <- Val >>. +encode_chars_compact_map(Val, NumBits, {Lb,Limit}) -> + << <<(enc_char_cm(C, Lb, Limit)):NumBits>> || C <- Val >>. + encode_chars_16bit(Val) -> L = [case C of {0,0,A,B} -> [A,B]; @@ -272,6 +278,25 @@ to_named_bitstring(Val, Lb) -> %% for correctness, not speed. adjust_trailing_zeroes(to_bitstring(Val), Lb). +is_default_bitstring(asn1_DEFAULT, _, _) -> + true; +is_default_bitstring(Named, Named, _) -> + true; +is_default_bitstring(Bs, _, Bs) -> + true; +is_default_bitstring(Val, _, Def) when is_bitstring(Val) -> + Sz = bit_size(Def), + case Val of + <<Def:Sz/bitstring,T/bitstring>> -> + NumZeroes = bit_size(T), + case T of + <<0:NumZeroes>> -> true; + _ -> false + end; + _ -> + false + end. + is_default_bitstring(asn1_DEFAULT, _, _, _, _) -> true; is_default_bitstring({Unused,Bin}, V0, V1, V2, V3) when is_integer(Unused) -> @@ -306,6 +331,16 @@ is_default_bitstring(_, _, _, _, _) -> false. extension_bitmap(Val, Pos, Limit) -> extension_bitmap(Val, Pos, Limit, 0). +open_type_to_binary({asn1_OPENTYPE,Bin}) when is_binary(Bin) -> + Bin. + +legacy_open_type_to_binary({asn1_OPENTYPE,Bin}) when is_binary(Bin) -> + Bin; +legacy_open_type_to_binary(Bin) when is_binary(Bin) -> + Bin; +legacy_open_type_to_binary(List) when is_list(List) -> + List. + %%% %%% Internal functions. %%% @@ -352,6 +387,15 @@ enc_char(C0, Lb, Tab) -> illegal_char_error() end. +enc_char_cm(C0, Lb, Limit) -> + C = C0 - Lb, + if + 0 =< C, C < Limit -> + C; + true -> + illegal_char_error() + end. + illegal_char_error() -> error({error,{asn1,"value forbidden by FROM constraint"}}). @@ -438,6 +482,8 @@ adjust_trailing_zeroes(Bs0, Lb) -> bs_drop_trailing_zeroes(Bs) -> bs_drop_trailing_zeroes(Bs, bit_size(Bs)). +bs_drop_trailing_zeroes(Bs, 0) -> + Bs; bs_drop_trailing_zeroes(Bs0, Sz0) when Sz0 < 8 -> <<Byte:Sz0>> = Bs0, Sz = Sz0 - ntz(Byte), diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 83bd66a631..888339a4d2 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -50,13 +50,14 @@ all() -> {group, performance}]. groups() -> - [{compile, parallel([]), + Parallel = asn1_test_lib:parallel(), + [{compile, Parallel, [c_syntax, c_string, c_implicit_before_choice, constraint_equivalence]}, - {ber, parallel([]), + {ber, Parallel, [ber_choiceinseq, % Uses 'SOpttest' ber_optional]}, @@ -65,7 +66,7 @@ groups() -> {appup_test, [], [{asn1_appup_test, all}]}, - {parallel, parallel([]), + {parallel, Parallel, [cover, xref, {group, ber}, @@ -133,14 +134,13 @@ groups() -> testChoiceIndefinite, per_open_type, testInfObjectClass, - testParameterizedInfObj, + testParam, testFragmented, testMergeCompile, testobj, testDeepTConstr, testExport, testImport, - testParamBasic, testDER, testDEFAULT, testMvrasn6, @@ -173,13 +173,6 @@ groups() -> testTimer_per, testTimer_uper]}]. -parallel(Options) -> - case erlang:system_info(smp_support) andalso - erlang:system_info(schedulers) > 1 of - true -> [parallel|Options]; - false -> Options - end. - %%------------------------------------------------------------------------------ %% Init/end %%------------------------------------------------------------------------------ @@ -328,6 +321,10 @@ testCompactBitString(Config, Rule, Opts) -> testPrimStrings(Config) -> test(Config, fun testPrimStrings/3, [ber,{ber,[der]},per,uper]). testPrimStrings(Config, Rule, Opts) -> + LegacyOpts = [legacy_erlang_types|Opts], + asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config, + [Rule|LegacyOpts]), + testPrimStrings_cases(Rule, LegacyOpts), asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config, [Rule|Opts]), testPrimStrings_cases(Rule, Opts), asn1_test_lib:compile_all(["PrimStrings", "BitStr"], Config, @@ -424,16 +421,15 @@ testMultipleLevels(Config, Rule, Opts) -> asn1_test_lib:compile("MultipleLevels", Config, [Rule|Opts]), testMultipleLevels:main(Rule). -testDef(Config) -> test(Config, fun testDef/3). -testDef(Config, Rule, Opts) -> - asn1_test_lib:compile("Def", Config, [Rule|Opts]), - testDef:main(Rule). - testDEFAULT(Config) -> test(Config, fun testDEFAULT/3, [ber,{ber,[der]},per,uper]). testDEFAULT(Config, Rule, Opts) -> asn1_test_lib:compile_all(["Def","Default"], Config, [Rule|Opts]), testDef:main(Rule), + testSeqSetDefaultVal:main(Rule, Opts), + asn1_test_lib:compile_all(["Def","Default"], Config, + [legacy_erlang_types,Rule|Opts]), + testDef:main(Rule), testSeqSetDefaultVal:main(Rule, Opts). testOpt(Config) -> test(Config, fun testOpt/3). @@ -523,12 +519,6 @@ testSetDefault(Config, Rule, Opts) -> asn1_test_lib:compile("SetDefault", Config, [Rule|Opts]), testSetDefault:main(Rule). -testParamBasic(Config) -> - test(Config, fun testParamBasic/3, [ber,{ber,[der]},per,uper]). -testParamBasic(Config, Rule, Opts) -> - asn1_test_lib:compile("ParamBasic", Config, [Rule|Opts]), - testParamBasic:main(Rule). - testSetOptional(Config) -> test(Config, fun testSetOptional/3). testSetOptional(Config, Rule, Opts) -> asn1_test_lib:compile("SetOptional", Config, [Rule|Opts]), @@ -761,12 +751,16 @@ testInfObjectClass(Config, Rule, Opts) -> testInfObjectClass:main(Rule), testInfObj:main(Rule). -testParameterizedInfObj(Config) -> - test(Config, fun testParameterizedInfObj/3). -testParameterizedInfObj(Config, Rule, Opts) -> - Files = ["Param","Param2"], +testParam(Config) -> + test(Config, fun testParam/3, [ber,{ber,[der]},per,uper]). +testParam(Config, Rule, Opts) -> + Files = ["ParamBasic","Param","Param2"], asn1_test_lib:compile_all(Files, Config, [Rule|Opts]), - testParameterizedInfObj:main(Config, Rule). + testParamBasic:main(Rule), + testParameterizedInfObj:main(Config, Rule), + asn1_test_lib:compile("Param", Config, + [legacy_erlang_types,Rule|Opts]), + testParameterizedInfObj:param(Rule). testFragmented(Config) -> test(Config, fun testFragmented/3). @@ -784,7 +778,8 @@ testMergeCompile(Config, Rule, Opts) -> testobj(Config) -> test(Config, fun testobj/3). testobj(Config, Rule, Opts) -> - asn1_test_lib:compile("RANAP", Config, [Rule|Opts]), + asn1_test_lib:compile("RANAP", Config, [legacy_erlang_types, + Rule|Opts]), asn1_test_lib:compile_erlang("testobj", Config, []), ok = testobj:run(), ok = testParameterizedInfObj:ranap(Rule). @@ -804,14 +799,15 @@ testExport(Config) -> testImport(Config) -> test(Config, fun testImport/3). testImport(Config, Rule, Opts) -> - {error, _} = asn1ct:compile(filename:join(?config(data_dir, Config), - "ImportsFrom"), - [Rule, {outdir, ?config(priv_dir, Config)} - |Opts]). + Files = ["ImportsFrom","ImportsFrom2","ImportsFrom3"], + asn1_test_lib:compile_all(Files, Config, [Rule|Opts]), + 42 = 'ImportsFrom':i(), + ok. testMegaco(Config) -> test(Config, fun testMegaco/3). testMegaco(Config, Rule, Opts) -> - {ok, Module1, Module2} = testMegaco:compile(Config, Rule, Opts), + {ok, Module1, Module2} = testMegaco:compile(Config, Rule, + [legacy_erlang_types|Opts]), ok = testMegaco:main(Module1, Config), ok = testMegaco:main(Module2, Config). @@ -850,7 +846,7 @@ duplicate_tags(Config) -> rtUI(Config) -> test(Config, fun rtUI/3). rtUI(Config, Rule, Opts) -> asn1_test_lib:compile("Prim", Config, [Rule|Opts]), - {ok, _} = asn1rt:info('Prim'), + _ = 'Prim':info(), Rule = 'Prim':encoding_rule(), io:format("Default BIT STRING format: ~p\n", ['Prim':bit_string_format()]). @@ -892,7 +888,8 @@ specialized_decodes(Config, Rule, Opts) -> "PartialDecMyHTTP.asn", "MEDIA-GATEWAY-CONTROL.asn", "P-Record"], - Config, [Rule, asn1config|Opts]), + Config, + [Rule,legacy_erlang_types,asn1config|Opts]), test_partial_incomplete_decode:test(Config), test_selective_decode:test(). @@ -1022,7 +1019,8 @@ test_x691(Config, Rule, Opts) -> test_x691:cases(Rule), %% OTP-7708. - asn1_test_lib:compile("EUTRA-extract-55", Config, [Rule|Opts]), + asn1_test_lib:compile("EUTRA-extract-55", Config, + [legacy_erlang_types,Rule|Opts]), %% OTP-7763. Val = {'Seq',15,lists:duplicate(8, 0),[0],lists:duplicate(28, 0),15,true}, @@ -1128,21 +1126,21 @@ END ok = asn1ct:compile(File, [{outdir, PrivDir}]). -timer_compile(Config, Rule, Opts) -> +timer_compile(Config, Rule) -> asn1_test_lib:compile_all(["H235-SECURITY-MESSAGES", "H323-MESSAGES"], - Config, [Rule|Opts]). + Config, [no_ok_wrapper,Rule]). testTimer_ber(Config) -> - timer_compile(Config,ber,[]), - testTimer:go(Config,ber). + timer_compile(Config, ber), + testTimer:go(). testTimer_per(Config) -> - timer_compile(Config,per,[]), - testTimer:go(Config,per). + timer_compile(Config, per), + testTimer:go(). testTimer_uper(Config) -> - timer_compile(Config,uper,[]), - {comment,_} = testTimer:go(Config,uper). + timer_compile(Config, uper), + testTimer:go(). %% Test of multiple-line comment, OTP-8043 testComment(suite) -> []; @@ -1200,8 +1198,8 @@ ticket_7407_code(FinalPadding) -> eutra1(msg) -> {'BCCH-BCH-Message', - {'MasterInformationBlock',[0,1,0,1],[1,0,1,0], - {'PHICH-Configuration',short,ffs},[1,0,1,0,0,0,0,0]}}. + {'MasterInformationBlock',<<2#0101:4>>,<<2#1010:4>>, + {'PHICH-Configuration',short,ffs},<<2#10100000>>}}. eutra1(result, true) -> <<90,80,0>>; diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py index c3b3aebd6d..3495cd841b 100644 --- a/lib/asn1/test/asn1_SUITE_data/Constraints.py +++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py @@ -16,6 +16,7 @@ SemiConstrained ::= INTEGER (100..MAX) NegSemiConstrained ::= INTEGER (-128..MAX) SemiConstrainedExt ::= INTEGER (42..MAX, ...) NegSemiConstrainedExt ::= INTEGER (-128..MAX, ...) +SemiNamed ::= INTEGER {a(100), b(200)} (100..MAX) -- Extensions -- LongLongExt ::= INTEGER (0..18446744073709551615, ..., -5000..-1) Range256to65536Ext ::= INTEGER (256..65536, ..., 1000000..9000000) @@ -65,10 +66,12 @@ Wednesday ::= Day(wednesday) Thing ::= INTEGER {fred (0),fred2 (1),fred3 (2)} - - AnotherThing ::= Thing (fred | fred2) +OneMoreThing ::= INTEGER {wilma(0), fred(1), betty(3), barney(2)} +OneMoreThing-1 ::= OneMoreThing (wilma | fred) +OneMoreThing-2 ::= OneMoreThing (fred | barney) + I ::= INTEGER (0|15..269) -- OTP-5457 X1 ::= INTEGER (1..4 | 8 | 10 | 20) -- OTP-9946 diff --git a/lib/asn1/test/asn1_SUITE_data/Default.asn b/lib/asn1/test/asn1_SUITE_data/Default.asn index 168ce50bb2..b91660381a 100644 --- a/lib/asn1/test/asn1_SUITE_data/Default.asn +++ b/lib/asn1/test/asn1_SUITE_data/Default.asn @@ -25,6 +25,10 @@ SeqBS ::= SEQUENCE { e BIT STRING DEFAULT '01011010'B } +SeqBS2 ::= SEQUENCE { + bs BIT STRING {a(0), z(25)} DEFAULT '101'B +} + SetBS ::= SET { a BIT STRING DEFAULT '1010110'B, b BIT STRING DEFAULT 'A8A'H, @@ -156,4 +160,23 @@ four INTEGER ::= 4 cr IA5String ::= {0,13} +SeqNamedInts ::= SEQUENCE { + i1 INTEGER {first(0), last(31)} DEFAULT 15, + i2 INTEGER {first(0), last(31)} DEFAULT 31 +} + +S5 ::= SEQUENCE { + s3 S3 DEFAULT {}, + so SEQUENCE OF OBJECT IDENTIFIER DEFAULT { + {itu-t question 999}, + {itu-t question 555} + }, + soe SEQUENCE OF OBJECT IDENTIFIER DEFAULT { } +} + +SOI ::= SEQUENCE { + soi SEQUENCE OF OBJECT IDENTIFIER + DEFAULT { {iso member-body f(250) 9 55}, {iso member-body f(250) 3 4} } +} + END diff --git a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 index 8dc5f3d7e1..74fa97e7aa 100644 --- a/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/EnumExt.asn1 @@ -18,6 +18,8 @@ Ext1 ::= ENUMERATED { magenta(9) } +SubExt1 ::= Ext1 ( blue | orange | black ) + Noext ::= ENUMERATED { blue(0), red(1), diff --git a/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1 b/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1 index 8c4f3a8f7e..b4ea943040 100644 --- a/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/INSTANCEOF.asn1 @@ -16,7 +16,9 @@ Names ::= SEQUENCE { thirdName [2] INSTANCE OF OTHER-NAME ({TI}) } -OTHER-NAME ::= TYPE-IDENTIFIER +OTHER-NAME ::= YET-ANOTHER-NAME + +YET-ANOTHER-NAME ::= TYPE-IDENTIFIER TI OTHER-NAME ::= {{INTEGER IDENTIFIED BY {2 4}} | {Seq IDENTIFIED BY {2 3 4}} | diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1 index 896a35d627..32b8f75dde 100644 --- a/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom.asn1 @@ -1,16 +1,8 @@ -ImportsFrom DEFINITIONS ::= - +ImportsFrom DEFINITIONS AUTOMATIC TAGS ::= BEGIN -IMPORTS -Type1, Type2, Type3 -FROM RemoteFile1 objid -val1, val2, val3 -FROM RemoteFile2; - -objid OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) remote-operations(4) notation(0)} - -LocalType ::= INTEGER +IMPORTS Int FROM ImportsFrom2; +i Int ::= 42 END diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1 new file mode 100644 index 0000000000..b0c29d24ae --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom2.asn1 @@ -0,0 +1,7 @@ +ImportsFrom2 DEFINITIONS AUTOMATIC TAGS ::= +BEGIN +IMPORTS Int FROM ImportsFrom3; + +LocalDef ::= OCTET STRING + +END diff --git a/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1 b/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1 new file mode 100644 index 0000000000..ca27b20697 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/ImportsFrom3.asn1 @@ -0,0 +1,4 @@ +ImportsFrom3 DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + Int ::= INTEGER (0..63) +END diff --git a/lib/asn1/test/asn1_SUITE_data/InfObj.asn b/lib/asn1/test/asn1_SUITE_data/InfObj.asn index 880e81c3b1..719119f418 100644 --- a/lib/asn1/test/asn1_SUITE_data/InfObj.asn +++ b/lib/asn1/test/asn1_SUITE_data/InfObj.asn @@ -255,6 +255,51 @@ Multiple-Optionals ::= SEQUENCE { t3 [2] MULTIPLE-OPTIONALS.&T3 ({Multiple-Optionals-Set}{@id}) OPTIONAL } +-- Test different ways of constructing object sets. + +OBJECT-SET-TEST ::= CLASS { + &id INTEGER UNIQUE, + &Type +} WITH SYNTAX { + &id IS &Type +} + +ObjectSetTest{OBJECT-SET-TEST:ObjectSet} ::= + SEQUENCE { + id OBJECT-SET-TEST.&id ({ObjectSet}), + type OBJECT-SET-TEST.&Type ({ObjectSet}{@id}) + } + +ost1 OBJECT-SET-TEST ::= { 1 IS BIT STRING } +ost2 OBJECT-SET-TEST ::= { 2 IS OCTET STRING } +ost3 OBJECT-SET-TEST ::= { 3 IS ENUMERATED {donald,scrooge} } +ost4 OBJECT-SET-TEST ::= { 4 IS BOOLEAN } +ost5 OBJECT-SET-TEST ::= { 5 IS INTEGER (0..15) } + +Ost12 OBJECT-SET-TEST ::= { ost1 | ost2 } +Ost123 OBJECT-SET-TEST ::= { ost3 | Ost12 } +Ost1234 OBJECT-SET-TEST ::= { Ost123 | ost4 } +Ost45 OBJECT-SET-TEST ::= { ost4 | ost5 } +Ost12345 OBJECT-SET-TEST ::= { Ost123 | Ost45 } + +OstSeq12 ::= ObjectSetTest{ {Ost12} } +OstSeq123 ::= ObjectSetTest{ {Ost123} } +OstSeq1234 ::= ObjectSetTest{ {Ost1234} } +OstSeq45 ::= ObjectSetTest{ {Ost45} } +OstSeq12345 ::= ObjectSetTest{ {Ost12345} } + +ExOst12 OBJECT-SET-TEST ::= { ost1, ..., ost2 } +ExOst123 OBJECT-SET-TEST ::= { ost3, ..., ExOst12 } +--ExOst1234 OBJECT-SET-TEST ::= { ExOst123, ..., ost4 } +ExOst45 OBJECT-SET-TEST ::= { ost4, ..., ost5 } +ExOst12345 OBJECT-SET-TEST ::= { ExOst123, ..., ExOst45 } + +ExOstSeq12 ::= ObjectSetTest{ {ExOst12} } +ExOstSeq123 ::= ObjectSetTest{ {ExOst123} } +--ExOstSeq1234 ::= ObjectSetTest{ {ExOst1234} } +ExOstSeq45 ::= ObjectSetTest{ {ExOst45} } +ExOstSeq12345 ::= ObjectSetTest{ {ExOst12345} } + END diff --git a/lib/asn1/test/asn1_SUITE_data/ParamBasic.asn1 b/lib/asn1/test/asn1_SUITE_data/ParamBasic.asn1 index 491bdf8956..68fc782f33 100644 --- a/lib/asn1/test/asn1_SUITE_data/ParamBasic.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/ParamBasic.asn1 @@ -20,4 +20,26 @@ T21 ::= General2{PrintableString} T22 ::= General2{BIT STRING} + +-- +-- Test a class parameter that is the governor for another parameter. +-- + +AlgorithmIdentifier{ALGORITHM-TYPE, ALGORITHM-TYPE:AlgorithmSet} ::= + SEQUENCE { + algorithm ALGORITHM-TYPE.&id ({AlgorithmSet}), + type ALGORITHM-TYPE.&Type ({AlgorithmSet}{@algorithm}) + } + +AnAlgorithm ::= AlgorithmIdentifier{ SIGNATURE-ALGORITHM, + { {KEY 1 CONTAINING INTEGER} | + {KEY 2 CONTAINING BOOLEAN} } } + +SIGNATURE-ALGORITHM ::= CLASS { + &id INTEGER UNIQUE, + &Type +} WITH SYNTAX { + KEY &id CONTAINING &Type +} + END diff --git a/lib/asn1/test/asn1_SUITE_data/SeqPrim.asn1 b/lib/asn1/test/asn1_SUITE_data/SeqPrim.asn1 index 20c4126c0b..7068674647 100644 --- a/lib/asn1/test/asn1_SUITE_data/SeqPrim.asn1 +++ b/lib/asn1/test/asn1_SUITE_data/SeqPrim.asn1 @@ -16,4 +16,11 @@ Seq ::= SEQUENCE Empty ::= SEQUENCE {} +Big ::= SEQUENCE { + ..., + os1 [1] OCTET STRING (SIZE (120..130)) OPTIONAL, + os2 [2] OCTET STRING (SIZE (128..256)) OPTIONAL, + os3 [3] OCTET STRING (SIZE (17000..30000)) OPTIONAL +} + END diff --git a/lib/asn1/test/asn1_SUITE_data/Set.py b/lib/asn1/test/asn1_SUITE_data/Set.py index 4062f6b804..3928004e6b 100644 --- a/lib/asn1/test/asn1_SUITE_data/Set.py +++ b/lib/asn1/test/asn1_SUITE_data/Set.py @@ -80,8 +80,8 @@ SetOpt3 ::= SET SetIn ::= SET { - boolIn BOOLEAN, - intIn INTEGER + boolIn BOOLEAN OPTIONAL, + intIn INTEGER OPTIONAL } diff --git a/lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl b/lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl index 06eba8b6eb..0bf4425263 100644 --- a/lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl +++ b/lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl @@ -47,7 +47,7 @@ val('TransactionPDU') -> dialoguePortion=val('DialoguePortion'), componentPortion=val('ComponentSequence')}; val('TransactionID') -> - "OCTET STRING"; + <<"OCTET STRING">>; val('DialoguePortion') -> #'DialoguePortion'{version=val('ProtocolVersion'), applicationContext={integerApplicationId,12}, @@ -57,23 +57,23 @@ val('DialoguePortion') -> val('Confidentiality') -> #'Confidentiality'{confidentialityId={integerConfidentialityId,14}}; val('ProtocolVersion') -> - "K"; + <<"K">>; val('UserInformation') -> [val('EXTERNAL'),val('EXTERNAL')]; val('EXTERNAL') -> #'EXTERNAL'{'direct-reference'={0,1,2}, - encoding={'single-ASN1-type',[1,2,3,4]}}; + encoding={'single-ASN1-type',<<1,2,3,4>>}}; val('ComponentSequence') -> [val('ComponentPDU',1),val('ComponentPDU',2),val('ComponentPDU',3)]; val('Invoke') -> - #'Invoke'{componentIDs="AB", + #'Invoke'{componentIDs = <<"AB">>, opcode={local,-2}, parameter=running}; val('ReturnResult') -> - #'ReturnResult'{componentID="C", + #'ReturnResult'{componentID = <<"C">>, parameter=[1,2,3,4]}; val('ReturnError') -> - #'ReturnError'{componentID="D", + #'ReturnError'{componentID = <<"D">>, errorCode={local,21}, parameter=true}; val('Abort') -> @@ -87,8 +87,8 @@ val(Type) -> check_result('PackageType',unidirectional,Res) -> {unidirectional, {'UniTransactionPDU', - "OCTET STRING", - {'DialoguePortion',"K", + <<"OCTET STRING">>, + {'DialoguePortion',<<"K">>, {integerApplicationId,12}, [_,%{'EXTERNAL',{syntax,{0,1,2}},asn1_NOVALUE,OTVal}, _],%{'EXTERNAL',{syntax,{0,1,2}},asn1_NOVALUE,OTVal}], @@ -96,14 +96,14 @@ check_result('PackageType',unidirectional,Res) -> {'Confidentiality', {integerConfidentialityId,14}}}, [{invokeLast, - {_,"AB",{local,-2},running}}, - {returnResultLast,{_,"C",_}}, - {returnError,{_,"D",{local,21},true}}]}} = Res, + {_,<<"AB">>,{local,-2},running}}, + {returnResultLast,{_,<<"C">>,_}}, + {returnError,{_,<<"D">>,{local,21},true}}]}} = Res, ok; %% check_OT_val(OTVal); check_result('PackageType',abort,Res)-> - {abort,{'Abort',"OCTET STRING", - {'DialoguePortion',"K", + {abort,{'Abort',<<"OCTET STRING">>, + {'DialoguePortion',<<"K">>, {integerApplicationId,12}, [_,%{'EXTERNAL',{syntax,{0,1,2}},asn1_NOVALUE,OTVal}, _],%{'EXTERNAL',{syntax,{0,1,2}},asn1_NOVALUE,OTVal}], @@ -114,9 +114,9 @@ check_result('PackageType',abort,Res)-> ok; %% check_OT_val(OTVal); check_result('PackageType',response,Res) -> - {response,{'TransactionPDU',"OCTET STRING", + {response,{'TransactionPDU',<<"OCTET STRING">>, {'DialoguePortion', - "K", + <<"K">>, {integerApplicationId,12}, [_,%{'EXTERNAL',{syntax,{0,1,2}},asn1_NOVALUE,OTVal}, _],%{'EXTERNAL',{syntax,{0,1,2}},asn1_NOVALUE,OTVal}], @@ -124,11 +124,11 @@ check_result('PackageType',response,Res) -> {'Confidentiality', {integerConfidentialityId,14}}}, [{invokeLast, - {_,"AB",{local,-2},running}}, + {_,<<"AB">>,{local,-2},running}}, {returnResultLast, - {_,"C",_}}, + {_,<<"C">>,_}}, {returnError, - {_,"D",{local,21},true}}]}} = Res, + {_,<<"D">>,{local,21},true}}]}} = Res, ok. %% check_OT_val(OTVal). diff --git a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl index 8e21e6ca84..a1e563f6be 100644 --- a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl +++ b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl @@ -67,8 +67,8 @@ run3(Erule) -> asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, asn1_NOVALUE,asn1_NOVALUE}, asn1_NOVALUE, - [[80,66,0,5,10,0,5,0,24,11,7,84,54,33,0,1,1,0,0,0,1,39,5,66,127,0,0,1], - []], + [<<80,66,0,5,10,0,5,0,24,11,7,84,54,33,0,1,1,0,0,0,1,39,5,66,127,0,0,1>>, + <<>>], {'RRC-RadioResourceConfigDedicated', [{'RRC-SRB-ToAddMod',1, {explicitValue, diff --git a/lib/asn1/test/asn1_app_test.erl b/lib/asn1/test/asn1_app_test.erl index 1225e36778..d800846b3f 100644 --- a/lib/asn1/test/asn1_app_test.erl +++ b/lib/asn1/test/asn1_app_test.erl @@ -134,13 +134,13 @@ get_ebin_mods(App) -> check_asn1ct_modules(Extra) -> ASN1CTMods = [asn1ct,asn1ct_check,asn1_db,asn1ct_pretty_format, - asn1ct_gen,asn1ct_gen_per,asn1ct_gen_per_rt2ct, + asn1ct_gen,asn1ct_gen_check,asn1ct_gen_per, asn1ct_name,asn1ct_constructed_per,asn1ct_constructed_ber, asn1ct_gen_ber,asn1ct_constructed_ber_bin_v2, asn1ct_gen_ber_bin_v2,asn1ct_value, asn1ct_tok,asn1ct_parser2,asn1ct_table, asn1ct_imm,asn1ct_func,asn1ct_rtt, - asn1ct_eval_ext,asn1ct_eval_per,asn1ct_eval_uper], + asn1ct_eval_ext], case Extra -- ASN1CTMods of [] -> ok; diff --git a/lib/asn1/test/asn1_appup_test.erl b/lib/asn1/test/asn1_appup_test.erl index a2c1423eda..7391959645 100644 --- a/lib/asn1/test/asn1_appup_test.erl +++ b/lib/asn1/test/asn1_appup_test.erl @@ -21,8 +21,8 @@ %% Purpose: Verify the application specifics of the asn1 application %%---------------------------------------------------------------------- -module(asn1_appup_test). --compile({no_auto_import,[error/1]}). -compile(export_all). +-include_lib("common_test/include/ct.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -44,16 +44,9 @@ end_per_group(_GroupName, Config) -> init_per_suite(suite) -> []; init_per_suite(doc) -> []; init_per_suite(Config) when is_list(Config) -> - AppFile = file_name(asn1, ".app"), - AppupFile = file_name(asn1, ".appup"), - [{app_file, AppFile}, {appup_file, AppupFile}|Config]. + Config. -file_name(App, Ext) -> - LibDir = code:lib_dir(App), - filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]). - - end_per_suite(suite) -> []; end_per_suite(doc) -> []; end_per_suite(Config) when is_list(Config) -> @@ -62,349 +55,7 @@ end_per_suite(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -appup(suite) -> - []; -appup(doc) -> - "perform a simple check of the appup file"; +appup() -> + [{doc, "perform a simple check of the asn1 appup file"}]. appup(Config) when is_list(Config) -> - AppupFile = key1search(appup_file, Config), - AppFile = key1search(app_file, Config), - Modules = modules(AppFile), - check_appup(AppupFile, Modules). - -modules(File) -> - case file:consult(File) of - {ok, [{application,asn1,Info}]} -> - case lists:keysearch(modules,1,Info) of - {value, {modules, Modules}} -> - Modules; - false -> - fail({bad_appinfo, Info}) - end; - Error -> - fail({bad_appfile, Error}) - end. - - -check_appup(AppupFile, Modules) -> - case file:consult(AppupFile) of - {ok, [{V, UpFrom, DownTo}]} -> - io:format("V= ~p, UpFrom= ~p, DownTo= ~p, Modules= ~p~n", - [V, UpFrom, DownTo, Modules]), - check_appup(V, UpFrom, DownTo, Modules); - Else -> - fail({bad_appupfile, Else}) - end. - - -check_appup(V, UpFrom, DownTo, Modules) -> - check_version(V), - check_depends(up, UpFrom, Modules), - check_depends(down, DownTo, Modules), - ok. - - -check_depends(_, [], _) -> - ok; -check_depends(UpDown, [Dep|Deps], Modules) -> - check_depend(UpDown, Dep, Modules), - check_depends(UpDown, Deps, Modules). - - -check_depend(up,I={add_application,_App},Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instruction: ~p" - "~n Modules: ~p", [up,I , Modules]), - ok; -check_depend(down,I={remove_application,_App},Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instruction: ~p" - "~n Modules: ~p", [down,I , Modules]), - ok; -check_depend(UpDown, {V, Instructions}, Modules) -> - d("check_instructions(~w) -> entry with" - "~n V: ~p" - "~n Modules: ~p", [UpDown, V, Modules]), - check_version(V), - case check_instructions(UpDown, - Instructions, Instructions, [], [], Modules) of - {_Good, []} -> - ok; - {_, Bad} -> - fail({bad_instructions, Bad, UpDown}) - end. - - -check_instructions(_, [], _, Good, Bad, _) -> - {lists:reverse(Good), lists:reverse(Bad)}; -check_instructions(UpDown, [Instr|Instrs], AllInstr, Good, Bad, Modules) -> - d("check_instructions(~w) -> entry with" - "~n Instr: ~p", [UpDown,Instr]), - case (catch check_instruction(UpDown, Instr, AllInstr, Modules)) of - ok -> - check_instructions(UpDown, Instrs, AllInstr, - [Instr|Good], Bad, Modules); - {error, Reason} -> - d("check_instructions(~w) -> bad instruction: " - "~n Reason: ~p", [UpDown,Reason]), - check_instructions(UpDown, Instrs, AllInstr, Good, - [{Instr, Reason}|Bad], Modules) - end. - -%% A new module is added -check_instruction(up, {add_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when up-add_module instruction with" - "~n Module: ~p", [Module]), - check_module(Module, Modules); - -%% An old module is re-added -check_instruction(down, {add_module, Module}, _, Modules) - when is_atom(Module) -> - d("check_instruction -> entry when down-add_module instruction with" - "~n Module: ~p", [Module]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - ok; - ok -> - error({existing_readded_module, Module}) - end; - -%% Removing a module on upgrade: -%% - the module has been removed from the app-file. -%% - check that no module depends on this (removed) module -check_instruction(up, {remove, {Module, Pre, Post}}, _, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post) -> - d("check_instruction -> entry when up-remove instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p", [Module, Pre, Post]), - case (catch check_module(Module, Modules)) of - {error, {unknown_module, Module, Modules}} -> - check_purge(Pre), - check_purge(Post); - ok -> - error({existing_removed_module, Module}) - end; - -%% Removing a module on downgrade: the module exist -%% in the app-file. -check_instruction(down, {remove, {Module, Pre, Post}}, AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post) -> - d("check_instruction -> entry when down-remove instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p", [Module, Pre, Post]), - case (catch check_module(Module, Modules)) of - ok -> - check_purge(Pre), - check_purge(Post), - check_no_remove_depends(Module, AllInstr); - {error, {unknown_module, Module, Modules}} -> - error({nonexisting_removed_module, Module}) - end; - -check_instruction(_, {load_module, Module, Pre, Post, Depend}, - AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - d("check_instruction -> entry when load_module instruction with" - "~n Module: ~p" - "~n Pre: ~p" - "~n Post: ~p" - "~n Depend: ~p", [Module, Pre, Post, Depend]), - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_module_depend(Module, Depend, updated_modules(AllInstr, [])), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, {update, Module, Change, Pre, Post, Depend}, - AllInstr, Modules) - when is_atom(Module), is_atom(Pre), is_atom(Post), is_list(Depend) -> - d("check_instruction -> entry when update instruction with" - "~n Module: ~p" - "~n Change: ~p" - "~n Pre: ~p" - "~n Post: ~p" - "~n Depend: ~p", [Module, Change, Pre, Post, Depend]), - check_module(Module, Modules), - check_module_depend(Module, Depend, Modules), - check_module_depend(Module, Depend, updated_modules(AllInstr, [])), - check_change(Change), - check_purge(Pre), - check_purge(Post); - -check_instruction(_, {apply, {Module, Function, Args}}, - _AllInstr, Modules) - when is_atom(Module), is_atom(Function), is_list(Args) -> - d("check_instruction -> entry when apply instruction with" - "~n Module: ~p" - "~n Function: ~p" - "~n Args: ~p", [Module, Function, Args]), - check_module(Module, Modules), - check_apply(Module,Function,Args); - -check_instruction(_, Instr, _AllInstr, _Modules) -> - d("check_instruction -> entry when unknown instruction with" - "~n Instr: ~p", [Instr]), - error({error, {unknown_instruction, Instr}}). - - -%% If Module X depends on Module Y, then module Y must have an update -%% instruction of some sort (otherwise the depend is faulty). -updated_modules([], Modules) -> - d("update_modules -> entry when done with" - "~n Modules: ~p", [Modules]), - Modules; -updated_modules([Instr|Instrs], Modules) -> - d("update_modules -> entry with" - "~n Instr: ~p" - "~n Modules: ~p", [Instr,Modules]), - Module = instruction_module(Instr), - d("update_modules -> Module: ~p", [Module]), - updated_modules(Instrs, [Module|Modules]). - -instruction_module({add_module, Module}) -> - Module; -instruction_module({remove, {Module, _, _}}) -> - Module; -instruction_module({load_module, Module, _, _, _}) -> - Module; -instruction_module({update, Module, _, _, _, _}) -> - Module; -instruction_module({apply, {Module, _, _}}) -> - Module; -instruction_module(Instr) -> - d("instruction_module -> entry when unknown instruction with" - "~n Instr: ~p", [Instr]), - error({error, {unknown_instruction, Instr}}). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -check_version(V) when is_list(V) -> - ok; -check_version(V) -> - error({bad_version, V}). - - -check_module(M, Modules) when is_atom(M) -> - case lists:member(M,Modules) of - true -> - ok; - false -> - error({unknown_module, M, Modules}) - end; -check_module(M, _) -> - error({bad_module, M}). - -check_apply(Module,Function,Args) -> - case (catch Module:module_info()) of - Info when is_list(Info) -> - check_exported(Function,Args,Info); - {'EXIT',{undef,_}} -> - error({not_existing_module,Module}) - end. - -check_exported(Function,Args,Info) -> - case lists:keysearch(exports,1,Info) of - {value,{exports,FunList}} -> - case lists:keysearch(Function,1,FunList) of - {value,{_,Arity}} when Arity==length(Args) -> - ok; - _ -> - error({not_exported_function,Function,length(Args)}) - end; - _ -> - error({bad_export,Info}) - end. - -check_module_depend(M, [], _) when is_atom(M) -> - d("check_module_depend -> entry with" - "~n M: ~p", [M]), - ok; -check_module_depend(M, Deps, Modules) when is_atom(M), is_list(Deps) -> - d("check_module_depend -> entry with" - "~n M: ~p" - "~n Deps: ~p" - "~n Modules: ~p", [M, Deps, Modules]), - case [Dep || Dep <- Deps, lists:member(Dep, Modules) == false] of - [] -> - ok; - Unknown -> - error({unknown_depend_modules, Unknown}) - end; -check_module_depend(_M, D, _Modules) -> - d("check_module_depend -> entry when bad depend with" - "~n D: ~p", [D]), - error({bad_depend, D}). - - -check_no_remove_depends(_Module, []) -> - ok; -check_no_remove_depends(Module, [Instr|Instrs]) -> - check_no_remove_depend(Module, Instr), - check_no_remove_depends(Module, Instrs). - -check_no_remove_depend(Module, {load_module, Mod, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - error({removed_module_in_depend, load_module, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(Module, {update, Mod, _Change, _Pre, _Post, Depend}) -> - case lists:member(Module, Depend) of - true -> - error({removed_module_in_depend, update, Mod, Module}); - false -> - ok - end; -check_no_remove_depend(_, _) -> - ok. - - -check_change(soft) -> - ok; -check_change({advanced, _Something}) -> - ok; -check_change(Change) -> - error({bad_change, Change}). - - -check_purge(soft_purge) -> - ok; -check_purge(brutal_purge) -> - ok; -check_purge(Purge) -> - error({bad_purge, Purge}). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -error(Reason) -> - throw({error, Reason}). - -fail(Reason) -> - exit({suite_failed, Reason}). - -key1search(Key, L) -> - case lists:keysearch(Key, 1, L) of - undefined -> - fail({not_found, Key, L}); - {value, {Key, Value}} -> - Value - end. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -d(F, A) -> - d(false, F, A). - -d(true, F, A) -> - io:format(F ++ "~n", A); -d(_, _, _) -> - ok. - - + ok = ?t:appup_test(asn1). diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl index 417380159e..da07cd1118 100644 --- a/lib/asn1/test/asn1_test_lib.erl +++ b/lib/asn1/test/asn1_test_lib.erl @@ -21,19 +21,72 @@ -export([compile/3,compile_all/3,compile_erlang/3, hex_to_bin/1, + parallel/0, roundtrip/3,roundtrip/4,roundtrip_enc/3,roundtrip_enc/4]). -include_lib("test_server/include/test_server.hrl"). +run_dialyzer() -> + false. + compile(File, Config, Options) -> compile_all([File], Config, Options). compile_all(Files, Config, Options) -> DataDir = ?config(data_dir, Config), CaseDir = ?config(case_dir, Config), - [compile_file(filename:join(DataDir, F), [{outdir, CaseDir}|Options]) + [compile_file(filename:join(DataDir, F), [{outdir, CaseDir}, + debug_info|Options]) || F <- Files], + dialyze(Files, Options), ok. +parallel() -> + case erlang:system_info(schedulers) > 1 andalso not run_dialyzer() of + true -> [parallel]; + false -> [] + end. + +dialyze(Files, Options) -> + case not run_dialyzer() orelse lists:member(abs, Options) of + true -> ok; + false -> dialyze(Files) + end. + +dialyze(Files) -> + Beams0 = [code:which(module(F)) || F <- Files], + Beams = [code:which(asn1rt_nif)|Beams0], + case dialyzer:run([{files,Beams}, + {warnings,[no_improper_lists]}, + {get_warnings,true}]) of + [] -> + ok; + [_|_]=Ws -> + io:put_chars([[B,$\n] || B <- Beams]), + io:put_chars([dialyzer:format_warning(W) || W <- Ws]), + error(dialyzer_warnings) + end. + +module(F0) -> + F1 = filename:basename(F0), + F2 = case filename:extension(F1) of + ".asn" -> + filename:rootname(F1); + ".asn1" -> + filename:rootname(F1); + ".py" -> + filename:rootname(F1); + "" -> + F1 + end, + F = case filename:extension(F2) of + ".set" -> + filename:rootname(F2); + "" -> + F2 + end, + list_to_atom(F). +%% filename:join(CaseDir, F ++ ".beam"). + compile_file(File, Options) -> try ok = asn1ct:compile(File, [warnings_as_errors|Options]) @@ -59,6 +112,7 @@ roundtrip(Mod, Type, Value) -> roundtrip(Mod, Type, Value, ExpectedValue) -> {ok,Encoded} = Mod:encode(Type, Value), {ok,ExpectedValue} = Mod:decode(Type, Encoded), + test_ber_indefinite(Mod, Type, Encoded, ExpectedValue), ok. roundtrip_enc(Mod, Type, Value) -> @@ -67,6 +121,7 @@ roundtrip_enc(Mod, Type, Value) -> roundtrip_enc(Mod, Type, Value, ExpectedValue) -> {ok,Encoded} = Mod:encode(Type, Value), {ok,ExpectedValue} = Mod:decode(Type, Encoded), + test_ber_indefinite(Mod, Type, Encoded, ExpectedValue), Encoded. %%% @@ -76,3 +131,52 @@ roundtrip_enc(Mod, Type, Value, ExpectedValue) -> hex2num(C) when $0 =< C, C =< $9 -> C - $0; hex2num(C) when $A =< C, C =< $F -> C - $A + 10; hex2num(C) when $a =< C, C =< $f -> C - $a + 10. + +test_ber_indefinite(Mod, Type, Encoded, ExpectedValue) -> + case Mod:encoding_rule() of + ber -> + Indefinite = iolist_to_binary(ber_indefinite(Encoded)), + {ok,ExpectedValue} = Mod:decode(Type, Indefinite); + _ -> + ok + end. + +%% Rewrite all definite lengths for constructed values to an +%% indefinite length. +ber_indefinite(Bin0) -> + case ber_get_tag(Bin0) of + done -> + []; + primitive -> + Bin0; + {constructed,Tag,Bin1} -> + {Len,Bin2} = ber_get_len(Bin1), + <<Val0:Len/binary,Bin/binary>> = Bin2, + Val = iolist_to_binary(ber_indefinite(Val0)), + [<<Tag/binary,16#80,Val/binary,0,0>>|ber_indefinite(Bin)] + end. + +ber_get_tag(<<>>) -> + done; +ber_get_tag(<<_:2,0:1,_:5,_/binary>>) -> + primitive; +ber_get_tag(<<_:2,1:1,_:5,_/binary>>=Bin0) -> + TagLen = ber_tag_length(Bin0), + <<Tag:TagLen/binary,Bin/binary>> = Bin0, + {constructed,Tag,Bin}. + +ber_tag_length(<<_:3,2#11111:5,T/binary>>) -> + ber_tag_length_1(T, 1); +ber_tag_length(_) -> + 1. + +ber_tag_length_1(<<1:1,_:7,T/binary>>, N) -> + ber_tag_length_1(T, N+1); +ber_tag_length_1(<<0:1,_:7,_/binary>>, N) -> + N+1. + +ber_get_len(<<0:1,L:7,T/binary>>) -> + {L,T}; +ber_get_len(<<1:1,Octets:7,T0/binary>>) -> + <<L:Octets/unit:8,T/binary>> = T0, + {L,T}. diff --git a/lib/asn1/test/ber_decode_error.erl b/lib/asn1/test/ber_decode_error.erl index 8be92292ee..6fd2450c62 100644 --- a/lib/asn1/test/ber_decode_error.erl +++ b/lib/asn1/test/ber_decode_error.erl @@ -51,4 +51,18 @@ run([]) -> {error,{asn1,{invalid_value,_}}} = (catch 'Constructed':decode('I', <<8,7>>)), + %% Short indefinite length. Make sure that the decoder doesn't look + %% beyond the end of binary when looking for a 0,0 terminator. + {error,{asn1,{invalid_length,_}}} = + (catch 'Constructed':decode('S', sub(<<8,16#80,0,0>>, 3))), + {error,{asn1,{invalid_length,_}}} = + (catch 'Constructed':decode('S', sub(<<8,16#80,0,0>>, 2))), + {error,{asn1,{invalid_length,_}}} = + (catch 'Constructed':decode('S', sub(<<40,16#80,1,1,255,0,0>>, 6))), + {error,{asn1,{invalid_length,_}}} = + (catch 'Constructed':decode('S', sub(<<40,16#80,1,1,255,0,0>>, 5))), ok. + +sub(Bin, Bytes) -> + <<B:Bytes/binary,_/binary>> = Bin, + B. diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl index 6451f81c01..1edd60f7c8 100644 --- a/lib/asn1/test/error_SUITE.erl +++ b/lib/asn1/test/error_SUITE.erl @@ -19,7 +19,9 @@ -module(error_SUITE). -export([suite/0,all/0,groups/0, - already_defined/1,enumerated/1,objects/1]). + already_defined/1,bitstrings/1,enumerated/1, + imports/1,instance_of/1,integers/1,objects/1, + parameterization/1,values/1]). -include_lib("test_server/include/test_server.hrl"). @@ -29,9 +31,16 @@ all() -> [{group,p}]. groups() -> - [{p,parallel(),[already_defined, - enumerated, - objects]}]. + [{p,parallel(), + [already_defined, + bitstrings, + enumerated, + imports, + instance_of, + integers, + objects, + parameterization, + values]}]. parallel() -> case erlang:system_info(schedulers) > 1 of @@ -68,6 +77,23 @@ already_defined(Config) -> } = run(P, Config), ok. +bitstrings(Config) -> + M = 'Bitstrings', + P = {M, + <<"Bitstrings DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + " Bs1 ::= BIT STRING {a(1), a(1)}\n" + " Bs2 ::= BIT STRING {a(1), b(2), a(3)}\n" + " Bs3 ::= BIT STRING {x(1), y(1)}\n" + " Bs4 ::= BIT STRING {x(-1), y(0)}\n" + "END\n">>}, + {error, + [{structured_error,{M,2},asn1ct_check,{namelist_redefinition,a}}, + {structured_error,{M,3},asn1ct_check,{namelist_redefinition,a}}, + {structured_error,{M,4},asn1ct_check,{value_reused,1}}, + {structured_error,{M,5},asn1ct_check,{invalid_bit_number,-1}} + ]} = run(P, Config), + ok. + enumerated(Config) -> M = 'Enumerated', P = {M, @@ -96,6 +122,63 @@ enumerated(Config) -> } = run(P, Config), ok. +imports(Config) -> + Ext = 'ExternalModule', + ExtP = {Ext, + <<"ExternalModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + "END\n">>}, + ok = run(ExtP, Config), + + M = 'Imports', + P = {M, + <<"Imports DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + "IMPORTS NotDefined FROM ExternalModule\n" + "X FROM UndefinedModule objid\n" + "Y, Z FROM UndefinedModule2;\n" + "objid OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) remote-operations(4)\n" + " notation(0)}\n" + "END\n">>}, + {error,[{structured_error,{M,2},asn1ct_check, + {undefined_import,'NotDefined','ExternalModule'}}, + {structured_error,{M,3},asn1ct_check,{undefined_import,'X','UndefinedModule'}}, + {structured_error,{M,4},asn1ct_check,{undefined_import,'Y','UndefinedModule2'}}, + {structured_error,{M,4},asn1ct_check,{undefined_import,'Z','UndefinedModule2'}} + ]} = run(P, Config), + ok. + +instance_of(Config) -> + M = 'InstanceOf', + P = {M, + <<"InstanceOf DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + "XX ::= INSTANCE OF CL ({TI})\n" + "CL ::= CLASS {\n" + "&id INTEGER,\n" + "&Type\n" + "}\n" + "o1 CL ::= {&id 1, &Type OCTET STRING}\n" + "TI CL ::= { o1 }\n" + "END\n">>}, + {error, + [{structured_error,{M,2},asn1ct_check,{illegal_instance_of,'CL'}} + ]} = run(P, Config), + ok. + +integers(Config) -> + M = 'Integers', + P = {M, + <<"Integers DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + " Int1 ::= INTEGER {a(1), a(1)}\n" + " Int2 ::= INTEGER {a(1), b(2), a(3)}\n" + " Int3 ::= INTEGER {x(1), y(1)}\n" + "END\n">>}, + {error, + [{structured_error,{M,2},asn1ct_check,{namelist_redefinition,a}}, + {structured_error,{M,3},asn1ct_check,{namelist_redefinition,a}}, + {structured_error,{M,4},asn1ct_check,{value_reused,1}} + ]} = run(P, Config), + ok. + + objects(Config) -> M = 'Objects', P = {M, @@ -138,6 +221,38 @@ objects(Config) -> } = run(P, Config), ok. +parameterization(Config) -> + M = 'Parameterization', + P = {M, + <<"Parameterization DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + " NotUppercase{lowercase} ::= INTEGER (lowercase)\n" + "END\n">>}, + {error, + [{structured_error,{'Parameterization',2},asn1ct_check, + {illegal_typereference,lowercase}} + ] + } = run(P, Config), + ok. + +values(Config) -> + M = 'Values', + P = {M, + <<"Values DEFINITIONS AUTOMATIC TAGS ::= BEGIN\n" + " os1 OCTET STRING ::= \"abc\"\n" + " os2 OCTET STRING ::= 42\n" + " os3 OCTET STRING ::= { 1, 3 }\n" + "END\n">>}, + {error, + [ + {structured_error,{M,2},asn1ct_check, + illegal_octet_string_value}, + {structured_error,{M,3},asn1ct_check, + illegal_octet_string_value}, + {structured_error,{M,4},asn1ct_check, + illegal_octet_string_value} + ] + } = run(P, Config), + ok. run({Mod,Spec}, Config) -> diff --git a/lib/asn1/test/h323test.erl b/lib/asn1/test/h323test.erl index 3baaa994ea..7577928493 100644 --- a/lib/asn1/test/h323test.erl +++ b/lib/asn1/test/h323test.erl @@ -42,7 +42,7 @@ alerting_val() -> {'TerminalInfo',asn1_NOVALUE}, false,false}, asn1_NOVALUE, - {'CallIdentifier',[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}, + {'CallIdentifier',<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>}, asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}}, asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}, asn1_NOVALUE}. @@ -57,18 +57,18 @@ connect_val() -> {'Connect-UUIE', {0,0,8,2250,0,2}, {ipAddress, - {'TransportAddress_ipAddress',[136,225,41,58],1187}}, + {'TransportAddress_ipAddress',<<136,225,41,58>>,1187}}, {'EndpointType',asn1_NOVALUE, {'VendorIdentifier', {'H221NonStandard',181,0,21324}, - [77,105,99,114,111,115,111,102,116,174,32,78,101,116, - 77,101,100,116,105,110,103,174,0], - [51,46,48,0]}, + <<77,105,99,114,111,115,111,102,116,174,32,78,101,116, + 77,101,100,116,105,110,103,174,0>>, + <<51,46,48,0>>}, asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, {'TerminalInfo',asn1_NOVALUE}, false,false}, - [22,137,237,197,191,35,211,17,140,45,0,192,79,75,28,208], - {'CallIdentifier',[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}, + <<22,137,237,197,191,35,211,17,140,45,0,192,79,75,28,208>>, + {'CallIdentifier',<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>}, asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}}, asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}, asn1_NOVALUE}. diff --git a/lib/asn1/test/testChoExtension.erl b/lib/asn1/test/testChoExtension.erl index e54cbe825b..09e19ceebb 100644 --- a/lib/asn1/test/testChoExtension.erl +++ b/lib/asn1/test/testChoExtension.erl @@ -31,13 +31,13 @@ extension(_Rules) -> %% A trick to encode with another compatible CHOICE type to test reception %% extension alternative - roundtrip('ChoExt1x', {str,"abc"}), + roundtrip('ChoExt1x', {str,<<"abc">>}), roundtrip('ChoExt2', {bool,true}), roundtrip('ChoExt2', {int,33}), roundtrip('ChoExt3', {bool,true}), roundtrip('ChoExt3', {int,33}), - roundtrip('ChoExt4', {str,"abc"}), + roundtrip('ChoExt4', {str,<<"abc">>}), roundtrip('ChoEmptyRoot', {bool,false}), roundtrip('ChoEmptyRoot', {bool,true}), diff --git a/lib/asn1/test/testChoExternal.erl b/lib/asn1/test/testChoExternal.erl index 12abdbb2bc..0914d54f33 100644 --- a/lib/asn1/test/testChoExternal.erl +++ b/lib/asn1/test/testChoExternal.erl @@ -31,16 +31,16 @@ external(_Rules) -> roundtrip('ChoXBool', {xboolImp,true}), roundtrip('ChoXBool', {xboolExp,true}), - roundtrip('NT', {os,"kalle"}), - roundtrip('Exp', {os,"kalle"}), - roundtrip('NTNT', {os,"kalle"}), - roundtrip('NTExp', {os,"kalle"}), - roundtrip('ExpNT', {os,"kalle"}), - roundtrip('ExpExp', {os,"kalle"}), - roundtrip('XNTNT', {os,"kalle"}), - roundtrip('XNTExp', {os,"kalle"}), - roundtrip('XExpNT', {os,"kalle"}), - roundtrip('XExpExp', {os,"kalle"}), + roundtrip('NT', {os,<<"kalle">>}), + roundtrip('Exp', {os,<<"kalle">>}), + roundtrip('NTNT', {os,<<"kalle">>}), + roundtrip('NTExp', {os,<<"kalle">>}), + roundtrip('ExpNT', {os,<<"kalle">>}), + roundtrip('ExpExp', {os,<<"kalle">>}), + roundtrip('XNTNT', {os,<<"kalle">>}), + roundtrip('XNTExp', {os,<<"kalle">>}), + roundtrip('XExpNT', {os,<<"kalle">>}), + roundtrip('XExpExp', {os,<<"kalle">>}), ok. diff --git a/lib/asn1/test/testChoOptional.erl b/lib/asn1/test/testChoOptional.erl index f5e77cb721..71a7346e3f 100644 --- a/lib/asn1/test/testChoOptional.erl +++ b/lib/asn1/test/testChoOptional.erl @@ -27,16 +27,20 @@ run() -> roundtrip('Seq1', #'Seq1'{bool=true,int=asn1_NOVALUE,cho=asn1_NOVALUE}), roundtrip('Seq1', #'Seq1'{bool=true,int=233,cho=asn1_NOVALUE}), - roundtrip('Seq1', #'Seq1'{bool=true,int=asn1_NOVALUE,cho={vsCho,"Vs Str"}}), - roundtrip('Seq1', #'Seq1'{bool=true,int=asn1_NOVALUE,cho={ocStrCho,"Oct Str"}}), + roundtrip('Seq1', #'Seq1'{bool=true,int=asn1_NOVALUE, + cho={vsCho,"Vs Str"}}), + roundtrip('Seq1', #'Seq1'{bool=true,int=asn1_NOVALUE, + cho={ocStrCho,<<"Oct Str">>}}), roundtrip('Seq2', #'Seq2'{int=asn1_NOVALUE,cho=asn1_NOVALUE,bool=true}), roundtrip('Seq2', #'Seq2'{int=233,cho=asn1_NOVALUE,bool=true}), roundtrip('Seq2', #'Seq2'{int=asn1_NOVALUE,cho={vsCho,"Vs Str"},bool=true}), - roundtrip('Seq2', #'Seq2'{int=asn1_NOVALUE,cho={ocStrCho,"Oct Str"},bool=true}), + roundtrip('Seq2', #'Seq2'{int=asn1_NOVALUE,cho={ocStrCho,<<"Oct Str">>}, + bool=true}), roundtrip('Seq3', #'Seq3'{cho=asn1_NOVALUE,int=asn1_NOVALUE,bool=true}), roundtrip('Seq3', #'Seq3'{cho=asn1_NOVALUE,int=233,bool=true}), roundtrip('Seq3', #'Seq3'{cho={vsCho,"Vs Str"},int=asn1_NOVALUE,bool=true}), - roundtrip('Seq3', #'Seq3'{cho={ocStrCho,"Oct Str"},int=asn1_NOVALUE,bool=true}), + roundtrip('Seq3', #'Seq3'{cho={ocStrCho,<<"Oct Str">>}, + int=asn1_NOVALUE,bool=true}), ok. roundtrip(Type, Value) -> diff --git a/lib/asn1/test/testChoRecursive.erl b/lib/asn1/test/testChoRecursive.erl index 593b845949..ccd60c2897 100644 --- a/lib/asn1/test/testChoRecursive.erl +++ b/lib/asn1/test/testChoRecursive.erl @@ -31,13 +31,13 @@ recursive(_Rules) -> roundtrip('ChoRec', {something, #'ChoRec_something'{a = 77, - b = "some octets here", + b = <<"some octets here">>, c = {nothing,'NULL'}}}), roundtrip('ChoRec', {nothing,'NULL'}), roundtrip('ChoRec2', {something, #'ChoRec2_something'{a = 77, - b = "some octets here", + b = <<"some octets here">>, c = {nothing,'NULL'}}}), roundtrip('ChoRec2', {nothing,'NULL'}), ok. diff --git a/lib/asn1/test/testChoTypeRefCho.erl b/lib/asn1/test/testChoTypeRefCho.erl index cd2672add0..636c301403 100644 --- a/lib/asn1/test/testChoTypeRefCho.erl +++ b/lib/asn1/test/testChoTypeRefCho.erl @@ -30,7 +30,7 @@ choice(_Rules) -> roundtrip('ChoTRcho', {'choChoE-E',{choInt,88}}), roundtrip('ChoChoInline', {bool1,true}), roundtrip('ChoChoInline', {choCho,{bool,true}}), - roundtrip('ChoChoInline', {choCho,{octStr,"kk"}}), + roundtrip('ChoChoInline', {choCho,{octStr,<<"kk">>}}), roundtrip('ChoChoInline', {choCho,{int,55}}), ok. diff --git a/lib/asn1/test/testChoTypeRefPrim.erl b/lib/asn1/test/testChoTypeRefPrim.erl index 8a2bc7bd8e..747baeddd8 100644 --- a/lib/asn1/test/testChoTypeRefPrim.erl +++ b/lib/asn1/test/testChoTypeRefPrim.erl @@ -25,18 +25,18 @@ prim(_Rules) -> roundtrip('ChoTR', {bool,true}), - roundtrip('ChoTR', {octStr,[11,12,13,14,15,16,17]}), + roundtrip('ChoTR', {octStr,<<11,12,13,14,15,16,17>>}), roundtrip('ChoTR', {int,233}), - roundtrip('ChoTR', {octStr,"Stringing in the rain"}), - roundtrip('ChoTR2', {octStr,"A string"}), - roundtrip('ChoTR2', {octStrI,"A string"}), - roundtrip('ChoTR2', {octStrE,"A string"}), - roundtrip('ChoTR2', {'octStr-I',"A string"}), - roundtrip('ChoTR2', {'octStrI-I',"A string"}), - roundtrip('ChoTR2', {'octStrE-I',"A string"}), - roundtrip('ChoTR2', {'octStr-E',"A string"}), - roundtrip('ChoTR2', {'octStrI-E',"A string"}), - roundtrip('ChoTR2', {'octStrE-E',"A string"}), + roundtrip('ChoTR', {octStr,<<"Stringing in the rain">>}), + roundtrip('ChoTR2', {octStr,<<"A string">>}), + roundtrip('ChoTR2', {octStrI,<<"A string">>}), + roundtrip('ChoTR2', {octStrE,<<"A string">>}), + roundtrip('ChoTR2', {'octStr-I',<<"A string">>}), + roundtrip('ChoTR2', {'octStrI-I',<<"A string">>}), + roundtrip('ChoTR2', {'octStrE-I',<<"A string">>}), + roundtrip('ChoTR2', {'octStr-E',<<"A string">>}), + roundtrip('ChoTR2', {'octStrI-E',<<"A string">>}), + roundtrip('ChoTR2', {'octStrE-E',<<"A string">>}), ok. roundtrip(Type, Value) -> diff --git a/lib/asn1/test/testChoTypeRefSeq.erl b/lib/asn1/test/testChoTypeRefSeq.erl index 86c22619aa..91d0b45e89 100644 --- a/lib/asn1/test/testChoTypeRefSeq.erl +++ b/lib/asn1/test/testChoTypeRefSeq.erl @@ -28,15 +28,24 @@ -record('ChoSeqExp', {seqInt, seqOs}). seq(_Rules) -> - roundtrip('ChoTRseq', {choSeq,#'ChoSeq'{seqInt=88,seqOs="A string"}}), - roundtrip('ChoTRseq', {choSeqI,#'ChoSeq'{seqInt=88,seqOs="A string"}}), - roundtrip('ChoTRseq', {choSeqE,#'ChoSeq'{seqInt=88,seqOs="A string"}}), - roundtrip('ChoTRseq', {'choSeq-I',#'ChoSeqImp'{seqInt=88,seqOs="A string"}}), - roundtrip('ChoTRseq', {'choSeqI-I',#'ChoSeqImp'{seqInt=88,seqOs="A string"}}), - roundtrip('ChoTRseq', {'choSeqE-I',#'ChoSeqImp'{seqInt=88,seqOs="A string"}}), - roundtrip('ChoTRseq', {'choSeq-E',#'ChoSeqExp'{seqInt=88,seqOs="A string"}}), - roundtrip('ChoTRseq', {'choSeqI-E',#'ChoSeqExp'{seqInt=88,seqOs="A string"}}), - roundtrip('ChoTRseq', {'choSeqE-E',#'ChoSeqExp'{seqInt=88,seqOs="A string"}}), + roundtrip('ChoTRseq', {choSeq,#'ChoSeq'{seqInt=88, + seqOs = <<"A string">>}}), + roundtrip('ChoTRseq', {choSeqI,#'ChoSeq'{seqInt=88, + seqOs = <<"A string">>}}), + roundtrip('ChoTRseq', {choSeqE,#'ChoSeq'{seqInt=88, + seqOs = <<"A string">>}}), + roundtrip('ChoTRseq', {'choSeq-I',#'ChoSeqImp'{seqInt=88, + seqOs = <<"A string">>}}), + roundtrip('ChoTRseq', {'choSeqI-I',#'ChoSeqImp'{seqInt=88, + seqOs = <<"A string">>}}), + roundtrip('ChoTRseq', {'choSeqE-I',#'ChoSeqImp'{seqInt=88, + seqOs = <<"A string">>}}), + roundtrip('ChoTRseq', {'choSeq-E',#'ChoSeqExp'{seqInt=88, + seqOs = <<"A string">>}}), + roundtrip('ChoTRseq', {'choSeqI-E',#'ChoSeqExp'{seqInt=88, + seqOs = <<"A string">>}}), + roundtrip('ChoTRseq', {'choSeqE-E',#'ChoSeqExp'{seqInt=88, + seqOs = <<"A string">>}}), ok. roundtrip(Type, Value) -> diff --git a/lib/asn1/test/testChoTypeRefSet.erl b/lib/asn1/test/testChoTypeRefSet.erl index fd3d75cbcb..bd9068b53e 100644 --- a/lib/asn1/test/testChoTypeRefSet.erl +++ b/lib/asn1/test/testChoTypeRefSet.erl @@ -28,15 +28,24 @@ -record('ChoSetExp', {setInt, setOs}). set(_Rules) -> - roundtrip('ChoTRset', {choSet,#'ChoSet'{setInt=88,setOs="A string"}}), - roundtrip('ChoTRset', {choSetI,#'ChoSet'{setInt=88,setOs="A string"}}), - roundtrip('ChoTRset', {choSetE,#'ChoSet'{setInt=88,setOs="A string"}}), - roundtrip('ChoTRset', {'choSet-I',#'ChoSetImp'{setInt=88,setOs="A string"}}), - roundtrip('ChoTRset', {'choSetI-I',#'ChoSetImp'{setInt=88,setOs="A string"}}), - roundtrip('ChoTRset', {'choSetE-I',#'ChoSetImp'{setInt=88,setOs="A string"}}), - roundtrip('ChoTRset', {'choSet-E',#'ChoSetExp'{setInt=88,setOs="A string"}}), - roundtrip('ChoTRset', {'choSetI-E',#'ChoSetExp'{setInt=88,setOs="A string"}}), - roundtrip('ChoTRset', {'choSetE-E',#'ChoSetExp'{setInt=88,setOs="A string"}}), + roundtrip('ChoTRset', {choSet,#'ChoSet'{setInt=88, + setOs = <<"A string">>}}), + roundtrip('ChoTRset', {choSetI,#'ChoSet'{setInt=88, + setOs = <<"A string">>}}), + roundtrip('ChoTRset', {choSetE,#'ChoSet'{setInt=88, + setOs = <<"A string">>}}), + roundtrip('ChoTRset', {'choSet-I',#'ChoSetImp'{setInt=88, + setOs = <<"A string">>}}), + roundtrip('ChoTRset', {'choSetI-I',#'ChoSetImp'{setInt=88, + setOs = <<"A string">>}}), + roundtrip('ChoTRset', {'choSetE-I',#'ChoSetImp'{setInt=88, + setOs = <<"A string">>}}), + roundtrip('ChoTRset', {'choSet-E',#'ChoSetExp'{setInt=88, + setOs = <<"A string">>}}), + roundtrip('ChoTRset', {'choSetI-E',#'ChoSetExp'{setInt=88, + setOs = <<"A string">>}}), + roundtrip('ChoTRset', {'choSetE-E',#'ChoSetExp'{setInt=88, + setOs = <<"A string">>}}), ok. roundtrip(Type, Value) -> diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index 54ba748519..3ccf883bd6 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -208,14 +208,14 @@ int_constraints(Rules) -> %% More SIZE Constraints %%========================================================== - roundtrip('FixedSize', "0123456789"), - roundtrip('FixedSize2', "0123456789"), - roundtrip('FixedSize2', "0123456789abcdefghij"), + roundtrip('FixedSize', <<"0123456789">>), + roundtrip('FixedSize2', <<"0123456789">>), + roundtrip('FixedSize2', <<"0123456789abcdefghij">>), range_error(Rules, 'FixedSize', "short"), range_error(Rules, 'FixedSize2', "short"), - [roundtrip('VariableSize', lists:seq($A, $A+L-1)) || + [roundtrip('VariableSize', list_to_binary(lists:seq($A, $A+L-1))) || L <- lists:seq(1, 10)], roundtrip_enc('ShorterExt', "a", shorter_ext(Rules, "a")), diff --git a/lib/asn1/test/testEnumExt.erl b/lib/asn1/test/testEnumExt.erl index c66adaf949..878518be11 100644 --- a/lib/asn1/test/testEnumExt.erl +++ b/lib/asn1/test/testEnumExt.erl @@ -59,6 +59,10 @@ main(ber) -> common(ber). common(Erule) -> + roundtrip('SubExt1', blue), + roundtrip('SubExt1', orange), + roundtrip('SubExt1', black), + roundtrip('Seq', {'Seq',blue,42}), roundtrip('Seq', {'Seq',red,42}), roundtrip('Seq', {'Seq',green,42}), diff --git a/lib/asn1/test/testFragmented.erl b/lib/asn1/test/testFragmented.erl index 8d5fa07a5b..35b21f90a9 100644 --- a/lib/asn1/test/testFragmented.erl +++ b/lib/asn1/test/testFragmented.erl @@ -22,10 +22,10 @@ -export([main/1]). main(_Erule) -> - roundtrip('PDU', {'PDU',1,false,["abc","def"]}), + roundtrip('PDU', {'PDU',1,false,[<<"abc">>,<<"def">>]}), B256 = lists:seq(0, 255), K1 = lists:duplicate(4, B256), - K8 = binary_to_list(iolist_to_binary(lists:duplicate(8, K1))), + K8 = iolist_to_binary(lists:duplicate(8, K1)), roundtrip('PDU', {'PDU',1,false,[K8,K8]}), roundtrip('PDU', {'PDU',1,false,[K8,K8,K8,K8]}), roundtrip('PDU', {'PDU',1,false,[K8,K8,K8,K8,K8,K8]}), diff --git a/lib/asn1/test/testInfObj.erl b/lib/asn1/test/testInfObj.erl index cd335e1023..37c134b1b9 100644 --- a/lib/asn1/test/testInfObj.erl +++ b/lib/asn1/test/testInfObj.erl @@ -49,7 +49,7 @@ main(_Erule) -> roundtrip('RANAPextract1', 'InitiatingMessage2', Val3), roundtrip('InfObj', 'MyPdu', {'MyPdu',42,12,false,"string"}), - roundtrip('InfObj', 'MyPdu', {'MyPdu',{'Seq',1023,"hello"}, + roundtrip('InfObj', 'MyPdu', {'MyPdu',{'Seq',1023,<<"hello">>}, 42,true,"longer string"}), roundtrip('InfObj', 'MyPdu', {'MyPdu',"75712346",43,true,"string"}), @@ -110,15 +110,49 @@ main(_Erule) -> enc_dec('InfObj', 'DefaultInSeq', {'DefaultInSeq',3,asn1_DEFAULT}), roundtrip('InfObj', 'Multiple-Optionals', - {'Multiple-Optionals',1,42,true,"abc"}), + {'Multiple-Optionals',1,42,true,<<"abc">>}), roundtrip('InfObj', 'Multiple-Optionals', - {'Multiple-Optionals',1,asn1_NOVALUE,true,"abc"}), + {'Multiple-Optionals',1,asn1_NOVALUE,true,<<"abc">>}), roundtrip('InfObj', 'Multiple-Optionals', - {'Multiple-Optionals',1,42,asn1_NOVALUE,"abc"}), + {'Multiple-Optionals',1,42,asn1_NOVALUE,<<"abc">>}), roundtrip('InfObj', 'Multiple-Optionals', {'Multiple-Optionals',1,42,true,asn1_NOVALUE}), roundtrip('InfObj', 'Multiple-Optionals', - {'Multiple-Optionals',1,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}). + {'Multiple-Optionals',1,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}), + + test_objset('OstSeq12', [1,2]), + test_objset('OstSeq123', [1,2,3]), + test_objset('OstSeq1234', [1,2,3,4]), + test_objset('OstSeq45', [4,5]), + test_objset('OstSeq12345', [1,2,3,4,5]), + + test_objset('ExOstSeq12', [1,2]), + test_objset('ExOstSeq123', [1,2,3]), + %%test_objset('ExOstSeq1234', [1,2,3,4]), + test_objset('ExOstSeq45', [4,5]), + test_objset('ExOstSeq12345', [1,2,3,4,5]), + + ok. + +test_objset(Type, Keys) -> + _ = [test_object(Type, Key) || Key <- Keys], + _ = [(catch test_object(Type, Key)) || + Key <- lists:seq(1, 5) -- Keys], + ok. + +test_object(T, 1) -> + roundtrip('InfObj', T, {T,1,<<42:7>>}); +test_object(T, 2) -> + roundtrip('InfObj', T, {T,2,<<"abc">>}); +test_object(T, 3) -> + roundtrip('InfObj', T, {T,3,donald}), + roundtrip('InfObj', T, {T,3,scrooge}); +test_object(T, 4) -> + roundtrip('InfObj', T, {T,4,true}), + roundtrip('InfObj', T, {T,4,false}); +test_object(T, 5) -> + roundtrip('InfObj', T, {T,5,0}), + roundtrip('InfObj', T, {T,5,15}). roundtrip(M, T, V) -> asn1_test_lib:roundtrip(M, T, V). diff --git a/lib/asn1/test/testMergeCompile.erl b/lib/asn1/test/testMergeCompile.erl index 7cda71c441..b21897cfc2 100644 --- a/lib/asn1/test/testMergeCompile.erl +++ b/lib/asn1/test/testMergeCompile.erl @@ -30,12 +30,12 @@ main(Erule) -> %% test of module MS.set.asn that tests OTP-4492: different tagdefault in %% modules and types with same name in modules - MSVal = {'Type4M2',8,true,three,"OCTET STRING"}, + MSVal = {'Type4M2',8,true,three,<<"OCTET STRING">>}, asn1_test_lib:roundtrip('MS', 'Type4M2', MSVal), %% test of RANAP.set.asn1 PIEVal2 = [{'ProtocolIE-Field',4,ignore,{radioNetwork,'rab-pre-empted'}}], - EncVal = + EncVal0 = case Erule of per -> <<1,100>>; @@ -44,6 +44,7 @@ main(Erule) -> ber -> <<2,1,1>> end, + EncVal = {asn1_OPENTYPE,EncVal0}, PEVal2 = [{'ProtocolExtensionField',1,ignore,EncVal}, {'ProtocolExtensionField',2,reject,EncVal}], Val2 = @@ -142,7 +143,40 @@ test('InsertSubscriberDataArg') -> ok. test(mvrasn6,'InsertSubscriberDataArg') -> - Val = {'InsertSubscriberDataArg',"IMSI","Address","C",serviceGranted,["abc","cde"],["tele","serv","ice"],asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,{'NAEA-PreferredCI',"NCC",asn1_NOVALUE},{'GPRSSubscriptionData','NULL',[{'PDP-Context',49,"PT","PDP-Address","QoS",'NULL',"APN",asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}],asn1_NOVALUE},'NULL',onlyMSC,{'LSAInformation','NULL',accessOutsideLSAsAllowed,[{'LSAData',"LSA","L",'NULL',asn1_NOVALUE},{'LSAData',"LSA","L",'NULL',asn1_NOVALUE}],asn1_NOVALUE},'NULL',{'LCSInformation',["Addr","ess","string"],[{'LCS-PrivacyClass',"S","ExtSS",notifyLocationAllowed,[{'ExternalClient',{'LCSClientExternalID',"Addr",asn1_NOVALUE},asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}],[broadcastService,anonymousLocation,targetMSsubscribedService],asn1_NOVALUE}],asn1_NOVALUE},100,"age",{'MC-SS-Info',"S","ExtSS",5,4,asn1_NOVALUE},"C",{'SGSN-CAMEL-SubscriptionInfo',{'GPRS-CSI',[{'GPRS-CamelTDPData',attach,13,"Addr",continueTransaction,asn1_NOVALUE}],11,asn1_NOVALUE,'NULL','NULL'},{'SMS-CSI',[{'SMS-CAMEL-TDP-DataList','sms-CollectedInfo',13,"Addr",continueTransaction,asn1_NOVALUE}],11,asn1_NOVALUE,'NULL','NULL'},asn1_NOVALUE},"ON"}, - {ok,Bytes} = 'Mvrasn6':encode('InsertSubscriberDataArg', Val), - {ok,_} = 'Mvrasn6':decode('InsertSubscriberDataArg', Bytes), + Val = {'InsertSubscriberDataArg',<<"IMSI">>,<<"Address">>,<<"C">>, + serviceGranted,[<<"abc">>,<<"cde">>], + [<<"tele">>,<<"serv">>,<<"ice">>], + asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, + asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE, + {'NAEA-PreferredCI',<<"NCC">>,asn1_NOVALUE}, + {'GPRSSubscriptionData','NULL', + [{'PDP-Context',49,<<"PT">>,<<"PDP-Address">>,<<"QoS">>, + 'NULL',<<"APN">>,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}], + asn1_NOVALUE},'NULL',onlyMSC, + {'LSAInformation','NULL',accessOutsideLSAsAllowed, + [{'LSAData',<<"LSA">>,<<"L">>,'NULL',asn1_NOVALUE}, + {'LSAData',<<"LSA">>,<<"L">>,'NULL',asn1_NOVALUE}], + asn1_NOVALUE},'NULL', + {'LCSInformation',[<<"Addr">>,<<"ess">>,<<"string">>], + [{'LCS-PrivacyClass',<<"S">>,<<"ExtSS">>,notifyLocationAllowed, + [{'ExternalClient', + {'LCSClientExternalID',<<"Addr">>,asn1_NOVALUE}, + asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE}], + [broadcastService,anonymousLocation,targetMSsubscribedService], + asn1_NOVALUE}],asn1_NOVALUE}, + 100,<<"age">>, + {'MC-SS-Info',<<"S">>,<<"ExtSS">>,5,4,asn1_NOVALUE}, + <<"C">>, + {'SGSN-CAMEL-SubscriptionInfo', + {'GPRS-CSI', + [{'GPRS-CamelTDPData',attach,13,<<"Addr">>, + continueTransaction,asn1_NOVALUE}], + 11,asn1_NOVALUE,'NULL','NULL'}, + {'SMS-CSI', + [{'SMS-CAMEL-TDP-Data','sms-CollectedInfo', + 13,<<"Addr">>,continueTransaction,asn1_NOVALUE}], + 11,asn1_NOVALUE,'NULL','NULL'}, + asn1_NOVALUE}, + <<"ON">>}, + asn1_test_lib:roundtrip('Mvrasn6', 'InsertSubscriberDataArg', Val), ok. diff --git a/lib/asn1/test/testNBAPsystem.erl b/lib/asn1/test/testNBAPsystem.erl index 57cb483374..e37e22163a 100644 --- a/lib/asn1/test/testNBAPsystem.erl +++ b/lib/asn1/test/testNBAPsystem.erl @@ -79,13 +79,14 @@ powerRaiseLimit, dLPowerAveragingWindowSize, 'iE-Extensions' = asn1_NOVALUE}). compile(Config, Options) -> - [asn1_test_lib:compile(filename:join([nbapsystem, M]), Config, Options) - || M <- ["NBAP-CommonDataTypes.asn", - "NBAP-IEs.asn", - "NBAP-PDU-Contents.asn", - "NBAP-PDU-Discriptions.asn", - "NBAP-Constants.asn", - "NBAP-Containers.asn"]], + Fs = [filename:join("nbapsystem", M) || + M <- ["NBAP-CommonDataTypes.asn", + "NBAP-IEs.asn", + "NBAP-PDU-Contents.asn", + "NBAP-PDU-Discriptions.asn", + "NBAP-Constants.asn", + "NBAP-Containers.asn"]], + asn1_test_lib:compile_all(Fs, Config, Options), ok. diff --git a/lib/asn1/test/testParamBasic.erl b/lib/asn1/test/testParamBasic.erl index 3db89ca174..39f7947e8d 100644 --- a/lib/asn1/test/testParamBasic.erl +++ b/lib/asn1/test/testParamBasic.erl @@ -43,6 +43,9 @@ main(Rules) -> #'T12'{number=11,string = <<10:4>>}); _ -> ok end, + roundtrip('AnAlgorithm', {'AnAlgorithm',1,42}), + roundtrip('AnAlgorithm', {'AnAlgorithm',2,true}), + roundtrip('AnAlgorithm', {'AnAlgorithm',2,false}), ok. roundtrip(Type, Value) -> diff --git a/lib/asn1/test/testParameterizedInfObj.erl b/lib/asn1/test/testParameterizedInfObj.erl index f3b4f9b170..2fe900792d 100644 --- a/lib/asn1/test/testParameterizedInfObj.erl +++ b/lib/asn1/test/testParameterizedInfObj.erl @@ -20,7 +20,7 @@ -module(testParameterizedInfObj). --export([main/2,ranap/1]). +-export([main/2,param/1,ranap/1]). -include_lib("test_server/include/test_server.hrl"). @@ -36,31 +36,29 @@ main(Config, Erule) -> param2(Config, Erule). param(Erule) -> - PERVal = #'AllocationOrRetentionPriority' - {priorityLevel = true, - iE_Extensions = - [#'ProtocolExtensionField'{id=14, - criticality=reject, - extensionValue= <<0>>}, - #'ProtocolExtensionField'{id=2, - criticality=ignore, - extensionValue= <<1>>}]}, - BERVal = #'AllocationOrRetentionPriority' - {priorityLevel = true, - iE_Extensions = - [#'ProtocolExtensionField'{id=14, - criticality=reject, - extensionValue= <<2,1,0>>}, - #'ProtocolExtensionField'{id=2, - criticality=ignore, - extensionValue= <<2,1,1>>}]}, - case Erule of - ber -> - roundtrip('AllocationOrRetentionPriority', BERVal); - per -> - roundtrip('AllocationOrRetentionPriority', PERVal); - uper -> - roundtrip('AllocationOrRetentionPriority', PERVal) + Exts0 = case Erule of + ber -> + %% As implemented, the open type must contain + %% valid BER-encoded data. + [{14,<<2,1,0>>},{2,<<2,1,0>>}]; + _ -> + %% The PER decoder will not look inside the open type. + [{14,<<0>>},{2,<<"anything goes">>}] + end, + case 'Param':legacy_erlang_types() of + false -> + Exts = [#'ProtocolExtensionField'{id=Id, + criticality=reject, + extensionValue={asn1_OPENTYPE, + Eval}} || + {Id,Eval} <- Exts0], + aor_roundtrip(Exts); + true -> + Exts = [#'ProtocolExtensionField'{id=Id, + criticality=reject, + extensionValue=Eval} || + {Id,Eval} <- Exts0], + aor_roundtrip(Exts) end, %% test code for OTP-4242, ValueFromObject @@ -72,8 +70,13 @@ param(Erule) -> {error,_Reason2} = 'Param':decode('OS2',[4,4,1,2,3,4]), {ok,_Val4} = 'Param':decode('OS1',[4,2,1,2]); _ -> %per/uper - roundtrip('OS1', [1,2]), - {error,_Reason3} = 'Param':encode('OS1', [1,2,3,4]) + case 'Param':legacy_erlang_types() of + false -> + roundtrip('OS1', <<1,2>>), + {error,_Reason3} = 'Param':encode('OS1', <<1,2,3,4>>); + true -> + ok + end end, roundtrip('Scl', {'Scl',42,{a,9738654}}), @@ -82,6 +85,11 @@ param(Erule) -> ok. +aor_roundtrip(Exts) -> + Val = #'AllocationOrRetentionPriority'{priorityLevel = true, + iE_Extensions = Exts}, + roundtrip('AllocationOrRetentionPriority', Val). + roundtrip(T, V) -> asn1_test_lib:roundtrip('Param', T, V). @@ -102,11 +110,11 @@ ranap(_Erule) -> param2(Config, Erule) -> roundtrip2('HandoverRequired', {'HandoverRequired', - [{'ProtocolIE-Field',1,"ABC"}, + [{'ProtocolIE-Field',1,<<"ABC">>}, {'ProtocolIE-Field',2,577799}]}), Enc = roundtrip2('HandoverRequired', {'HandoverRequired', - [{'ProtocolIE-Field',1,"ABC"}, + [{'ProtocolIE-Field',1,<<"ABC">>}, {'ProtocolIE-Field',2,-42}, {'ProtocolIE-Field',100,533}, {'ProtocolIE-Field',101,true}]}), @@ -127,17 +135,19 @@ param2(Config, Erule) -> [{i,DataDir},{outdir,CaseDir},Erule]), %% Decompile extended data. - {ok,{'HandoverRequired',[{'ProtocolIE-Field',1,"ABC"}, + {ok,{'HandoverRequired',[{'ProtocolIE-Field',1,<<"ABC">>}, {'ProtocolIE-Field',2,-42}, - {'ProtocolIE-Field',100,Open100}, - {'ProtocolIE-Field',101,Open101}]}} = + {'ProtocolIE-Field',100, + {asn1_OPENTYPE,Open100}}, + {'ProtocolIE-Field',101, + {asn1_OPENTYPE,Open101}}]}} = 'Param2':decode('HandoverRequired', Enc), true = is_binary(Open100), true = is_binary(Open101), %% Test single root. roundtrip2('SingleRoot', - {'SingleRoot',[{'ProtocolIE-Field',1,"ABC"}, + {'SingleRoot',[{'ProtocolIE-Field',1,<<"ABC">>}, {'ProtocolIE-Field',2,9999}]}), ok. diff --git a/lib/asn1/test/testPrimExternal.erl b/lib/asn1/test/testPrimExternal.erl index 07a1de931f..a03760976d 100644 --- a/lib/asn1/test/testPrimExternal.erl +++ b/lib/asn1/test/testPrimExternal.erl @@ -45,6 +45,6 @@ external(_Rules) -> 'XExpNT', 'XExpImp', 'XExpExp'], - _ = [asn1_test_lib:roundtrip('PrimExternal', T, "kalle") || + _ = [asn1_test_lib:roundtrip('PrimExternal', T, <<"kalle">>) || T <- Types], ok. diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl index 2fe0780701..155d6f6ff5 100644 --- a/lib/asn1/test/testPrimStrings.erl +++ b/lib/asn1/test/testPrimStrings.erl @@ -18,6 +18,8 @@ %% %% -module(testPrimStrings). +-compile([{nowarn_deprecated_function,{asn1rt,utf8_list_to_binary,1}}, + {nowarn_deprecated_function,{asn1rt,utf8_binary_to_list,1}}]). -export([bit_string/2]). -export([octet_string/1]). @@ -34,14 +36,11 @@ fragmented(Rules) -> Lens = fragmented_lengths(), - fragmented_octet_string(Rules, Lens), - case Rules of - per -> - %% NYI. - ok; - _ -> - fragmented_strings(Lens) - end. + case 'PrimStrings':legacy_erlang_types() of + false -> fragmented_octet_string(Rules, Lens); + true -> ok + end, + fragmented_strings(Lens). fragmented_strings(Lens) -> Types = ['Ns','Ps','Ps11','Vis','IA5'], @@ -74,33 +73,37 @@ bit_string(Rules, Opts) -> %% Bs1 ::= BIT STRING %%========================================================== - bs_roundtrip('Bs1', 0, <<>>), - bs_roundtrip('Bs1', 4, <<1:3>>), - bs_roundtrip('Bs1', 15, <<15:4>>), - bs_roundtrip('Bs1', 255, <<255:8>>), - - bs_roundtrip('Bs1', 256, [0,0,0,0,0,0,0,0,1]), - bs_roundtrip('Bs1', 257, [1,0,0,0,0,0,0,0,1]), - bs_roundtrip('Bs1', 444, [0,0,1,1,1,1,0,1,1]), - - {ok,Enc1} = 'PrimStrings':encode('Bs1', 12345678901234567890), - {ok,_} = 'PrimStrings':decode('Bs1', Enc1), + bs_roundtrip('Bs1', <<>>), + bs_roundtrip('Bs1', <<1:3>>), + bs_roundtrip('Bs1', <<15:4>>), + bs_roundtrip('Bs1', <<2#010010:6>>), + bs_roundtrip('Bs1', <<2#11111111:8>>), + bs_roundtrip('Bs1', <<2#100000000:9>>), + bs_roundtrip('Bs1', <<2#100000001:9>>), + bs_roundtrip('Bs1', <<2#001111011:9>>), + bs_roundtrip('Bs1', <<2#0100101111100010011:19>>), - bs_roundtrip('Bs1', [1,1,1,1,1,1,1,1]), - bs_roundtrip('Bs1', [0,1,0,0,1,0]), - bs_roundtrip('Bs1', [1,0,0,0,0,0,0,0,0]), - bs_roundtrip('Bs1', [0,1,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,1,1]), + case 'PrimStrings':legacy_erlang_types() of + false -> + ok; + true -> + {ok,Enc1} = 'PrimStrings':encode('Bs1', 12345678901234567890), + {ok,_} = 'PrimStrings':decode('Bs1', Enc1) + end, case {Rules,Opts} of - {ber,[]} -> + {ber,[legacy_erlang_types]} -> bs_decode('Bs1', <<35,8,3,2,0,73,3,2,4,32>>, [0,1,0,0,1,0,0,1,0,0,1,0]), bs_decode('Bs1', <<35,9,3,2,0,234,3,3,7,156,0>>, [1,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0]), bs_decode('Bs1', <<35,128,3,2,0,234,3,3,7,156,0,0,0>>, [1,1,1,0,1,0,1,0,1,0,0,1,1,1,0,0,0]); - _ -> + {ber,[]} -> + %% XXX + ok; + {_,_} -> %% DER, PER, UPER consistent_def_enc('BsDef1', [2#111101, @@ -120,30 +123,39 @@ bit_string(Rules, Opts) -> %%========================================================== roundtrip('Bs2', [mo,tu,fr]), - roundtrip('Bs2', [0,1,1,0,0,1,0], [mo,tu,fr]), + bs_roundtrip('Bs2', <<2#0110010:7>>, [mo,tu,fr]), + bs_roundtrip('Bs2', <<2#0110011:7>>, [mo,tu,fr,sa]), %%========================================================== %% Bs3 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (1..7)) %%========================================================== roundtrip('Bs3', [mo,tu,fr]), - bs_roundtrip('Bs3', [0,1,1,0,0,1,0], [mo,tu,fr]), + bs_roundtrip('Bs3', <<2#0110010:7>>, [mo,tu,fr]), + bs_roundtrip('Bs3', <<2#0110010:7>>, [mo,tu,fr]), + bs_roundtrip('Bs2', <<2#0110011:7>>, [mo,tu,fr,sa]), + bs_roundtrip('Bs3', <<2#011001:6>>, [mo,tu,fr]), + bs_roundtrip('Bs3', <<2#11:2>>, [su,mo]), %%========================================================== %% Bs7 ::= BIT STRING (SIZE (24)) %%========================================================== - bs_roundtrip('Bs7', 53245, - [1,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,0,0,0,0]), - bs_roundtrip('Bs7', [1,0,1,0], - [1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]), + bs_roundtrip('Bs7', <<23563:24>>), + case 'PrimStrings':legacy_erlang_types() of + false -> +%% {error,_} = 'PrimStrings':encode('Bs7', <<2#1010:4>>); + ok; + true -> + ok + end, %%========================================================== %% BsPri ::= [PRIVATE 61] BIT STRING %%========================================================== - bs_roundtrip('BsPri', 45, [1,0,1,1,0,1]), - bs_roundtrip('BsPri', 211, [1,1,0,0,1,0,1,1]), + bs_roundtrip('BsPri', <<2#101101:6>>), + bs_roundtrip('BsPri', <<2#11001011:8>>), case Rules of ber -> @@ -164,8 +176,8 @@ bit_string(Rules, Opts) -> %% BsExpPri ::= [PRIVATE 61] EXPLICIT BIT STRING %%========================================================== - bs_roundtrip('BsExpPri', 45, [1,0,1,1,0,1]), - bs_roundtrip('BsExpPri', 211, [1,1,0,0,1,0,1,1]), + bs_roundtrip('BsExpPri', <<2#101101:6>>), + bs_roundtrip('BsExpPri', <<2#11001011:8>>), case Rules of ber -> @@ -186,14 +198,14 @@ bit_string(Rules, Opts) -> %% veteran(2), collegeGraduate(3)}, test case for OTP-5710 %%========================================================== - {ok,Bytes54} = 'BitStr':encode('PersonalStatus', []), + {ok,Bytes54} = 'BitStr':encode('PersonalStatus', <<>>), {ok,[]} = 'BitStr':decode('PersonalStatus', Bytes54), %%========================================================== %% BS5932 ::= BIT STRING (SIZE (5..MAX)) %% test case for OTP-5932 %%========================================================== - bs_roundtrip('BSMAX', [1,0,1,0,1]), + bs_roundtrip('BSMAX', <<2#10101:5>>), case Rules of ber -> {error,_} = 'PrimStrings':encode('BSMAX', [1,0,1]); @@ -207,28 +219,35 @@ bit_string(Rules, Opts) -> %% BS1024 ::= BIT STRING (SIZE (1024)) %% test case for OTP-7602 %%========================================================== - BSmaker = - fun(_F,S,S,_,Acc) -> - Acc; - (F,Ix,S,{A,B},Acc) -> - F(F,Ix+1,S,{B,A},[A|Acc]) - end, - - BSList255 = BSmaker(BSmaker,0,255,{1,0},[]), - bs_roundtrip('BS255', BSList255), - BSList256 = BSmaker(BSmaker,0,256,{1,0},[]), - bs_roundtrip('BS256', BSList256), - BSList1024 = BSmaker(BSmaker,0,1024,{1,0},[]), - bs_roundtrip('BS1024', BSList1024), - bs_roundtrip('TransportLayerAddress', [0,1,1,0]), + bs_roundtrip('BS255', random_bits(255)), + bs_roundtrip('BS256', random_bits(256)), + bs_roundtrip('BS1024', random_bits(1024)), + + bs_roundtrip('TransportLayerAddress', <<2#0110:4>>), case Rules of ber -> ok; _ -> per_bs_strings() end. -consistent_def_enc(Type, Vs) -> +random_bits(N) -> + Seed = integer_to_list(erlang:phash2(erlang:now())), + random_bits(<<>>, N, Seed). + +random_bits(Bin, N, Seed) -> + RandomBits = erlang:md5(Seed), + Bits = bit_size(RandomBits), + if + Bits < N -> + random_bits(<<Bin/bitstring,RandomBits/bitstring>>, + N-Bits, RandomBits); + true -> + <<LastBits:N/bitstring,_/bitstring>> = RandomBits, + <<Bin/bitstring,LastBits/bitstring>> + end. + +consistent_def_enc(Type, Vs0) -> M = 'PrimStrings', {ok,Enc} = M:encode(Type, {Type,asn1_DEFAULT}), {ok,Val} = M:decode(Type, Enc), @@ -241,6 +260,13 @@ consistent_def_enc(Type, Vs) -> {legacy,{_,Bs}} when is_list(Bs) -> ok end, + %% If this is not the legacy format, only bitstrings are + %% allowed. + Vs = case M:legacy_erlang_types() of + false -> [V || V <- Vs0, is_bitstring(V)]; + true -> Vs0 + end, + %% All values should be recognized and encoded as the %% the default value (i.e. not encoded at all). _ = [{ok,Enc} = M:encode(Type, {Type,V}) || V <- Vs], @@ -252,18 +278,9 @@ consistent_def_enc(Type, Vs) -> %% a SIZE constraint). per_bs_strings() -> - bs_roundtrip('Bs3', [0,0,1,0,0,0,0], [tu]), bs_roundtrip('Bs3', <<2#0010000:7>>, [tu]), - bs_roundtrip('Bs3', {1,<<2#00100000:8>>}, [tu]), - - bs_roundtrip('Bs4', [0,1,1,0,0,1,0], [mo,tu,fr]), bs_roundtrip('Bs4', <<2#0110010:7>>, [mo,tu,fr]), - bs_roundtrip('Bs4', {1,<<2#01100100:8>>}, [mo,tu,fr]), - - bs_roundtrip('Bs4', [0,1,1,0,0,0,0], [mo,tu]), bs_roundtrip('Bs4', <<2#011:3,0:32>>, [mo,tu]), - bs_roundtrip('Bs4', {5,<<2#011:3,0:32,0:5>>}, [mo,tu]), - [per_trailing_zeroes(B) || B <- lists:seq(0, 255)], ok. @@ -279,10 +296,6 @@ per_trailing_zeroes(Byte) -> {bit,LastBitPos} -> LastBitPos+1 end, - %% List of zeroes and ones. - named_roundtrip(L, Pos, ExpectedSz), - named_roundtrip(L++[0,0,0,0,0], Pos, ExpectedSz), - %% Bitstrings. Bs = << <<B:1>> || B <- L >>, Sz = bit_size(Bs), @@ -290,14 +303,22 @@ per_trailing_zeroes(Byte) -> Bin = <<Bs:Sz/bits,0:16,0:7>>, named_roundtrip(Bin, Pos, ExpectedSz), - %% Compact bitstring. - named_roundtrip({7,Bin}, Pos, ExpectedSz), + case 'PrimStrings':legacy_erlang_types() of + false -> + ok; + true -> + %% List of zeroes and ones. + named_roundtrip(L, Pos, ExpectedSz), + named_roundtrip(L++[0,0,0,0,0], Pos, ExpectedSz), - %% Integer bitstring (obsolete). - IntBs = intlist_to_integer(L, 0, 0), - named_roundtrip(IntBs, Pos, ExpectedSz), + %% Compact bitstring. + named_roundtrip({7,Bin}, Pos, ExpectedSz), - ok. + %% Integer bitstring (obsolete). + IntBs = intlist_to_integer(L, 0, 0), + named_roundtrip(IntBs, Pos, ExpectedSz), + ok + end. make_bit_list(0) -> []; make_bit_list(B) -> [B band 1|make_bit_list(B bsr 1)]. @@ -331,61 +352,62 @@ octet_string(Rules) -> %% Os ::= OCTET STRING %%========================================================== + Legacy = 'PrimStrings':legacy_erlang_types(), case Rules of - ber -> - {ok,"Jones"} = + ber when not Legacy -> + {ok,<<"Jones">>} = 'PrimStrings':decode('Os', <<4,5,16#4A,16#6F,16#6E,16#65,16#73>>), - {ok,"Jones"} = + {ok,<<"Jones">>} = 'PrimStrings':decode('Os', <<36,9,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73>>), - {ok,"Jones"} = + {ok,<<"Jones">>} = 'PrimStrings':decode('Os', <<36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0>>), ok; _ -> ok end, - roundtrip('Os', [47,23,99,255,1]), - roundtrip('OsCon', [47,23,99,255,1]), - roundtrip('OsPri', [47,23,99,255,1]), - roundtrip('OsApp', [47,23,99,255,1]), + os_roundtrip('Os', <<47,23,99,255,1>>), + os_roundtrip('OsCon', <<47,23,99,255,1>>), + os_roundtrip('OsPri', <<47,23,99,255,1>>), + os_roundtrip('OsApp', <<47,23,99,255,1>>), - roundtrip('OsExpCon', [47,23,99,255,1]), - roundtrip('OsExpPri', [47,23,99,255,1]), - roundtrip('OsExpApp', [47,23,99,255,1]), + os_roundtrip('OsExpCon', <<47,23,99,255,1>>), + os_roundtrip('OsExpPri', <<47,23,99,255,1>>), + os_roundtrip('OsExpApp', <<47,23,99,255,1>>), - roundtrip('Os', []), - roundtrip('OsApp', []), - roundtrip('OsExpApp',[]), + os_roundtrip('Os', <<>>), + os_roundtrip('OsApp', <<>>), + os_roundtrip('OsExpApp', <<>>), - OsR = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + OsR = <<"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890">>, - roundtrip('Os', OsR), - roundtrip('OsCon', OsR), - roundtrip('OsExpApp', OsR), + os_roundtrip('Os', OsR), + os_roundtrip('OsCon', OsR), + os_roundtrip('OsExpApp', OsR), case Rules of - ber -> - {ok,"Jones"} = 'PrimStrings':decode('OsExpApp', <<127,62,7,4,5,16#4A,16#6F,16#6E,16#65,16#73>>), - {ok,"Jones"} = 'PrimStrings':decode('OsExpApp', <<127,62,11,36,9,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73>>), - {ok,"Jones"} = 'PrimStrings':decode('OsExpApp', <<127,62,13,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0>>), - {ok,"Jones"} = 'PrimStrings':decode('OsExpApp', <<127,62,128,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,0,0>>), - {ok,"JonesJones"} = 'PrimStrings':decode('OsExpApp', <<127,62,128,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,0,0>>), + ber when not Legacy -> + {ok,<<"Jones">>} = 'PrimStrings':decode('OsExpApp', <<127,62,7,4,5,16#4A,16#6F,16#6E,16#65,16#73>>), + {ok,<<"Jones">>} = 'PrimStrings':decode('OsExpApp', <<127,62,11,36,9,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73>>), + {ok,<<"Jones">>} = 'PrimStrings':decode('OsExpApp', <<127,62,13,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0>>), + {ok,<<"Jones">>} = 'PrimStrings':decode('OsExpApp', <<127,62,128,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,0,0>>), + {ok,<<"JonesJones">>} = 'PrimStrings':decode('OsExpApp', <<127,62,128,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,36,128,4,3,16#4A,16#6F,16#6E,4,2,16#65,16#73,0,0,0,0>>), ok; - - _-> + _ -> ok end, S255 = lists:seq(1, 255), - Strings = {type,true,"","1","12","345",true, - S255,[$a|S255],[$a,$b|S255],397}, - p_roundtrip('OsFixedStrings', Strings), - p_roundtrip('OsFixedStringsExt', Strings), - p_roundtrip('OsVarStringsExt', Strings), + Strings = {type,true,<<"">>,<<"1">>,<<"12">>,<<"345">>,true, + list_to_binary(S255),list_to_binary([$a|S255]), + list_to_binary([$a,$b|S255]),397}, + p_os_roundtrip('OsFixedStrings', Strings), + p_os_roundtrip('OsFixedStringsExt', Strings), + p_os_roundtrip('OsVarStringsExt', Strings), ShortenedStrings = shorten_by_two(Strings), - p_roundtrip('OsFixedStringsExt', ShortenedStrings), - p_roundtrip('OsVarStringsExt', ShortenedStrings), + p_os_roundtrip('OsFixedStringsExt', ShortenedStrings), + p_os_roundtrip('OsVarStringsExt', ShortenedStrings), ok. fragmented_octet_string(Erules, Lens) -> @@ -414,13 +436,14 @@ fragmented_octet_string(Erules, Types, L) -> ok. enc_frag(Erules, Type, Value) -> - {ok,Encoded} = 'PrimStrings':encode(Type, Value), + M = 'PrimStrings', + {ok,Encoded} = M:encode(Type, Value), case Erules of ber -> Encoded; _ -> %% Validate encoding with our own encoder. - Encoded = enc_frag_1(<<>>, list_to_binary(Value)) + Encoded = enc_frag_1(<<>>, Value) end. enc_frag_1(Res, Bin0) -> @@ -439,12 +462,12 @@ enc_frag_1(Res, Bin0) -> end. make_value(L) -> - make_value(L, 0, []). + make_value(L, 0, <<>>). make_value(0, _, Acc) -> Acc; make_value(N, Byte, Acc) when Byte =< 255 -> - make_value(N-1, Byte+7, [Byte|Acc]); + make_value(N-1, Byte+7, <<Acc/binary,Byte:8>>); make_value(N, Byte, Acc) -> make_value(N, Byte band 16#FF, Acc). @@ -742,10 +765,32 @@ utf8_string(_Rules) -> shorten_by_two(Tuple) -> L = [case E of [_,_|T] -> T; + <<_:16,T/binary>> -> T; _ -> E end || E <- tuple_to_list(Tuple)], list_to_tuple(L). +p_os_roundtrip(Type, Value0) -> + Value = setelement(1, Value0, Type), + p_os_roundtrip_1(Type, Value). + +p_os_roundtrip_1(Type, Value) -> + M = 'PrimStrings', + case M:legacy_erlang_types() of + false -> + asn1_test_lib:roundtrip(M, Type, Value); + true -> + {ok,Encoded} = M:encode(Type, Value), + Es0 = tuple_to_list(Value), + Es1 = [if + is_binary(E) -> binary_to_list(E); + true -> E + end || E <- Es0], + ListValue = list_to_tuple(Es1), + {ok,Encoded} = M:encode(Type, ListValue), + {ok,ListValue} = M:decode(Type, Encoded) + end. + p_roundtrip(Type, Value0) -> Value = setelement(1, Value0, Type), roundtrip(Type, Value). @@ -759,15 +804,57 @@ roundtrip(Type, Value, Expected) -> bs_roundtrip(Type, Value) -> bs_roundtrip(Type, Value, Value). -bs_roundtrip(Type, Value, Expected) -> +os_roundtrip(Type, Bin) when is_binary(Bin) -> M = 'PrimStrings', - {ok,Encoded} = M:encode(Type, Value), - {ok,Encoded} = M:encode(Type, Expected), - case M:decode(Type, Encoded) of - {ok,Expected} -> - ok; - {ok,Other} -> - Expected = convert(Other, Expected) + case M:legacy_erlang_types() of + false -> + asn1_test_lib:roundtrip(M, Type, Bin); + true -> + {ok,Encoded} = M:encode(Type, Bin), + List = binary_to_list(Bin), + {ok,Encoded} = M:encode(Type, List), + {ok,List} = M:decode(Type, Encoded) + end. + +bs_roundtrip(Type, Value, Expected) when is_bitstring(Value) -> + M = 'PrimStrings', + case M:legacy_erlang_types() of + false -> + asn1_test_lib:roundtrip(M, Type, Value, Expected); + true -> + {ok,Encoded} = M:encode(Type, Value), + BitList = [B || <<B:1>> <= Value], + {ok,Encoded} = M:encode(Type, BitList), + case BitList of + [] -> + {ok,Encoded} = M:encode(Type, 0); + [_|_] -> + case lists:last(BitList) of + 1 -> + Int = lists:foldr(fun(B, A) -> + (A bsl 1) bor B + end, 0, BitList), + {ok,Encoded} = M:encode(Type, Int); + 0 -> + %% This BIT STRING cannot be represented + %% as an integer. + ok + end + end, + Compact = case bit_size(Value) of + Bits when Bits rem 8 =:= 0 -> + {0,Value}; + Bits -> + Unused = 8 - Bits rem 8, + {Unused,<<Value:Bits/bitstring,0:Unused>>} + end, + {ok,Encoded} = M:encode(Type, Compact), + case M:decode(Type, Encoded) of + {ok,Expected} -> + ok; + {ok,Other} -> + Expected = convert(Other, Expected) + end end. bs_decode(Type, Encoded, Expected) -> diff --git a/lib/asn1/test/testSeqExtension.erl b/lib/asn1/test/testSeqExtension.erl index 8473459c36..c16e9fcd4c 100644 --- a/lib/asn1/test/testSeqExtension.erl +++ b/lib/asn1/test/testSeqExtension.erl @@ -44,7 +44,7 @@ main(Erule, DataDir, Opts) -> roundtrip('SeqExt4', #'SeqExt4'{bool=true,int=12345}), roundtrip('SeqExt4', #'SeqExt4'{bool=false,int=123456}), - roundtrip('SeqExt5', #'SeqExt5'{name="Arne",shoesize=47}), + roundtrip('SeqExt5', #'SeqExt5'{name = <<"Arne">>,shoesize=47}), %% Encode a value with this version of the specification. BigInt = 128638468966, @@ -52,7 +52,7 @@ main(Erule, DataDir, Opts) -> s2=#'SeqExt2'{bool=true,int=2345}, s3=#'SeqExt3'{bool=false,int=17}, s4=#'SeqExt4'{bool=true,int=38739739}, - s5=#'SeqExt5'{name="Arne",shoesize=47}, + s5=#'SeqExt5'{name = <<"Arne">>,shoesize=47}, s6=#'SeqExt6'{i1=531,i2=601,i3=999, i4=777,i5=11953, i6=13553,i7=77777}, diff --git a/lib/asn1/test/testSeqExternal.erl b/lib/asn1/test/testSeqExternal.erl index a8e0902244..0b1d305054 100644 --- a/lib/asn1/test/testSeqExternal.erl +++ b/lib/asn1/test/testSeqExternal.erl @@ -29,15 +29,15 @@ -record('SeqXSet3',{bool, int, set}). main(_Rules) -> - roundtrip('XNTNT', #'XSeqNT'{os="kalle",bool=true}), - roundtrip('XImpNT', #'XSeqNT'{os="kalle",bool=true}), - roundtrip('XExpNT', #'XSeqNT'{os="kalle",bool=true}), - roundtrip('XNTImp', #'XSeqImp'{os="kalle",bool=true}), - roundtrip('XImpImp', #'XSeqImp'{os="kalle",bool=true}), - roundtrip('XExpImp', #'XSeqImp'{os="kalle",bool=true}), - roundtrip('XNTExp', #'XSeqExp'{os="kalle",bool=true}), - roundtrip('XImpExp', #'XSeqExp'{os="kalle",bool=true}), - roundtrip('XExpExp', #'XSeqExp'{os="kalle",bool=true}), + roundtrip('XNTNT', #'XSeqNT'{os = <<"kalle">>,bool=true}), + roundtrip('XImpNT', #'XSeqNT'{os = <<"kalle">>,bool=true}), + roundtrip('XExpNT', #'XSeqNT'{os = <<"kalle">>,bool=true}), + roundtrip('XNTImp', #'XSeqImp'{os = <<"kalle">>,bool=true}), + roundtrip('XImpImp', #'XSeqImp'{os = <<"kalle">>,bool=true}), + roundtrip('XExpImp', #'XSeqImp'{os = <<"kalle">>,bool=true}), + roundtrip('XNTExp', #'XSeqExp'{os = <<"kalle">>,bool=true}), + roundtrip('XImpExp', #'XSeqExp'{os = <<"kalle">>,bool=true}), + roundtrip('XExpExp', #'XSeqExp'{os = <<"kalle">>,bool=true}), roundtrip('SeqXSet1', #'SeqXSet1'{set=#'XSet1'{bool1=true,int1=77, set1=#'XSetIn'{boolIn=false,intIn=88}}, diff --git a/lib/asn1/test/testSeqOf.erl b/lib/asn1/test/testSeqOf.erl index 7f8f4079b4..25059d6052 100644 --- a/lib/asn1/test/testSeqOf.erl +++ b/lib/asn1/test/testSeqOf.erl @@ -85,10 +85,10 @@ main(_Rules) -> seq43=SeqIn3}), roundtrip('Seq5', {'Seq5',true,[],77}), - roundtrip('Seq5', {'Seq5',true,[""],77}), - roundtrip('Seq5', {'Seq5',true,["a"],77}), - roundtrip('Seq5', {'Seq5',true,["ab"],77}), - roundtrip('Seq5', {'Seq5',true,["abc"],77}), + roundtrip('Seq5', {'Seq5',true,[<<"">>],77}), + roundtrip('Seq5', {'Seq5',true,[<<"a">>],77}), + roundtrip('Seq5', {'Seq5',true,[<<"ab">>],77}), + roundtrip('Seq5', {'Seq5',true,[<<"abc">>],77}), roundtrip('Seq6', {'Seq6',[],[],101}), roundtrip('Seq6', {'Seq6',[],[7],101}), @@ -100,15 +100,15 @@ main(_Rules) -> roundtrip('Seq8', {'Seq8',[],37}), roundtrip('Seq9', {'Seq9',true,[],97}), - roundtrip('Seq9', {'Seq9',true,[""],97}), - roundtrip('Seq9', {'Seq9',true,["x"],97}), - roundtrip('Seq9', {'Seq9',true,["xy"],97}), - roundtrip('Seq9', {'Seq9',true,["xyz"],97}), - - roundtrip('Seq10', {'Seq10',true,[""],97}), - roundtrip('Seq10', {'Seq10',true,["a"],97}), - roundtrip('Seq10', {'Seq10',true,["a","b"],97}), - roundtrip('Seq10', {'Seq10',true,["a","b","c"],97}), + roundtrip('Seq9', {'Seq9',true,[<<"">>],97}), + roundtrip('Seq9', {'Seq9',true,[<<"x">>],97}), + roundtrip('Seq9', {'Seq9',true,[<<"xy">>],97}), + roundtrip('Seq9', {'Seq9',true,[<<"xyz">>],97}), + + roundtrip('Seq10', {'Seq10',true,[<<"">>],97}), + roundtrip('Seq10', {'Seq10',true,[<<"a">>],97}), + roundtrip('Seq10', {'Seq10',true,[<<"a">>,<<"b">>],97}), + roundtrip('Seq10', {'Seq10',true,[<<"a">>,<<"b">>,<<"c">>],97}), roundtrip('SeqEmp', #'SeqEmp'{seq1=[#'Empty'{}]}), diff --git a/lib/asn1/test/testSeqOfExternal.erl b/lib/asn1/test/testSeqOfExternal.erl index 2e60f441c1..38b9f0ce7c 100644 --- a/lib/asn1/test/testSeqOfExternal.erl +++ b/lib/asn1/test/testSeqOfExternal.erl @@ -29,50 +29,59 @@ main(_Rules) -> roundtrip('NTNT', - [#'NT'{os="kalle",bool=true},#'NT'{os="kalle",bool=true}]), + [#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]), roundtrip('ImpNT', - [#'NT'{os="kalle",bool=true},#'NT'{os="kalle",bool=true}]), + [#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]), roundtrip('ExpNT', - [#'NT'{os="kalle",bool=true},#'NT'{os="kalle",bool=true}]), + [#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]), roundtrip('NTImp', - [#'Imp'{os="kalle",bool=true},#'Imp'{os="kalle",bool=true}]), + [#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]), roundtrip('ImpImp', - [#'Imp'{os="kalle",bool=true},#'Imp'{os="kalle",bool=true}]), + [#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]), roundtrip('ExpImp', - [#'Imp'{os="kalle",bool=true},#'Imp'{os="kalle",bool=true}]), + [#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]), roundtrip('NTExp', - [#'Exp'{os="kalle",bool=true},#'Exp'{os="kalle",bool=true}]), + [#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]), roundtrip('ImpExp', - [#'Exp'{os="kalle",bool=true},#'Exp'{os="kalle",bool=true}]), + [#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]), roundtrip('ExpExp', - [#'Exp'{os="kalle",bool=true},#'Exp'{os="kalle",bool=true}]), + [#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]), roundtrip('XNTNT', - [#'XSeqNT'{os="kalle",bool=true}, - #'XSeqNT'{os="kalle",bool=true}]), + [#'XSeqNT'{os = <<"kalle">>,bool=true}, + #'XSeqNT'{os = <<"kalle">>,bool=true}]), roundtrip('XImpNT', - [#'XSeqNT'{os="kalle",bool=true}, - #'XSeqNT'{os="kalle",bool=true}]), + [#'XSeqNT'{os = <<"kalle">>,bool=true}, + #'XSeqNT'{os = <<"kalle">>,bool=true}]), roundtrip('XExpNT', - [#'XSeqNT'{os="kalle",bool=true}, - #'XSeqNT'{os="kalle",bool=true}]), + [#'XSeqNT'{os = <<"kalle">>,bool=true}, + #'XSeqNT'{os = <<"kalle">>,bool=true}]), roundtrip('XNTImp', - [#'XSeqImp'{os="kalle",bool=true}, - #'XSeqImp'{os="kalle",bool=true}]), + [#'XSeqImp'{os = <<"kalle">>,bool=true}, + #'XSeqImp'{os = <<"kalle">>,bool=true}]), roundtrip('XImpImp', - [#'XSeqImp'{os="kalle",bool=true}, - #'XSeqImp'{os="kalle",bool=true}]), + [#'XSeqImp'{os = <<"kalle">>,bool=true}, + #'XSeqImp'{os = <<"kalle">>,bool=true}]), roundtrip('XExpImp', - [#'XSeqImp'{os="kalle",bool=true}, - #'XSeqImp'{os="kalle",bool=true}]), + [#'XSeqImp'{os = <<"kalle">>,bool=true}, + #'XSeqImp'{os = <<"kalle">>,bool=true}]), roundtrip('XNTExp', - [#'XSeqExp'{os="kalle",bool=true}, - #'XSeqExp'{os="kalle",bool=true}]), + [#'XSeqExp'{os = <<"kalle">>,bool=true}, + #'XSeqExp'{os = <<"kalle">>,bool=true}]), roundtrip('XImpExp', - [#'XSeqExp'{os="kalle",bool=true}, - #'XSeqExp'{os="kalle",bool=true}]), + [#'XSeqExp'{os = <<"kalle">>,bool=true}, + #'XSeqExp'{os = <<"kalle">>,bool=true}]), roundtrip('XExpExp', - [#'XSeqExp'{os="kalle",bool=true}, - #'XSeqExp'{os="kalle",bool=true}]), + [#'XSeqExp'{os = <<"kalle">>,bool=true}, + #'XSeqExp'{os = <<"kalle">>,bool=true}]), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSeqOfTag.erl b/lib/asn1/test/testSeqOfTag.erl index 38c1dcb90f..f66e29e91d 100644 --- a/lib/asn1/test/testSeqOfTag.erl +++ b/lib/asn1/test/testSeqOfTag.erl @@ -44,47 +44,47 @@ -record('Exp',{os, bool}). main(_Rules) -> - roundtrip('SeqTagNt', #'SeqTagNt'{nt=[#'NT'{os="kalle",bool=true}, - #'NT'{os="kalle",bool=true}]}), - roundtrip('SeqTagNtI', #'SeqTagNtI'{imp=[#'Imp'{os="kalle",bool=true}, - #'Imp'{os="kalle",bool=true}]}), - roundtrip('SeqTagNtE', #'SeqTagNtE'{exp=[#'Exp'{os="kalle",bool=true}, - #'Exp'{os="kalle",bool=true}]}), - roundtrip('SeqTagI', #'SeqTagI'{nt=[#'NT'{os="kalle",bool=true}, - #'NT'{os="kalle",bool=true}]}), - roundtrip('SeqTagII', #'SeqTagII'{imp=[#'Imp'{os="kalle",bool=true}, - #'Imp'{os="kalle",bool=true}]}), - roundtrip('SeqTagIE', #'SeqTagIE'{exp=[#'Exp'{os="kalle",bool=true}, - #'Exp'{os="kalle",bool=true}]}), - roundtrip('SeqTagE', #'SeqTagE'{nt=[#'NT'{os="kalle",bool=true}, - #'NT'{os="kalle",bool=true}]}), - roundtrip('SeqTagEI', #'SeqTagEI'{imp=[#'Imp'{os="kalle",bool=true}, - #'Imp'{os="kalle",bool=true}]}), - roundtrip('SeqTagEE', #'SeqTagEE'{exp=[#'Exp'{os="kalle",bool=true}, - #'Exp'{os="kalle",bool=true}]}), + roundtrip('SeqTagNt', #'SeqTagNt'{nt=[#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]}), + roundtrip('SeqTagNtI', #'SeqTagNtI'{imp=[#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SeqTagNtE', #'SeqTagNtE'{exp=[#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SeqTagI', #'SeqTagI'{nt=[#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]}), + roundtrip('SeqTagII', #'SeqTagII'{imp=[#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SeqTagIE', #'SeqTagIE'{exp=[#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SeqTagE', #'SeqTagE'{nt=[#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]}), + roundtrip('SeqTagEI', #'SeqTagEI'{imp=[#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SeqTagEE', #'SeqTagEE'{exp=[#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]}), roundtrip('SeqTagXNt', - #'SeqTagXNt'{xnt=[#'XSeqNT'{os="kalle",bool=true}, - #'XSeqNT'{os="kalle",bool=true}]}), + #'SeqTagXNt'{xnt=[#'XSeqNT'{os = <<"kalle">>,bool=true}, + #'XSeqNT'{os = <<"kalle">>,bool=true}]}), roundtrip('SeqTagXI', - #'SeqTagXI'{ximp=[#'XSeqImp'{os="kalle",bool=true}, - #'XSeqImp'{os="kalle",bool=true}]}), + #'SeqTagXI'{ximp=[#'XSeqImp'{os = <<"kalle">>,bool=true}, + #'XSeqImp'{os = <<"kalle">>,bool=true}]}), roundtrip('SeqTagXE', - #'SeqTagXE'{xexp=[#'XSeqExp'{os="kalle",bool=true}, - #'XSeqExp'{os="kalle",bool=true}]}), + #'SeqTagXE'{xexp=[#'XSeqExp'{os = <<"kalle">>,bool=true}, + #'XSeqExp'{os = <<"kalle">>,bool=true}]}), roundtrip('SeqTagImpX', - #'SeqTagImpX'{xnt=[#'XSeqNT'{os="kalle",bool=true}, - #'XSeqNT'{os="kalle",bool=true}], - ximp=[#'XSeqImp'{os="kalle",bool=true}, - #'XSeqImp'{os="kalle",bool=true}], - xexp=[#'XSeqExp'{os="kalle",bool=true}, - #'XSeqExp'{os="kalle",bool=true}]}), + #'SeqTagImpX'{xnt=[#'XSeqNT'{os = <<"kalle">>,bool=true}, + #'XSeqNT'{os = <<"kalle">>,bool=true}], + ximp=[#'XSeqImp'{os = <<"kalle">>,bool=true}, + #'XSeqImp'{os = <<"kalle">>,bool=true}], + xexp=[#'XSeqExp'{os = <<"kalle">>,bool=true}, + #'XSeqExp'{os = <<"kalle">>,bool=true}]}), roundtrip('SeqTagExpX', - #'SeqTagExpX'{xnt=[#'XSeqNT'{os="kalle",bool=true}, - #'XSeqNT'{os="kalle",bool=true}], - ximp=[#'XSeqImp'{os="kalle",bool=true}, - #'XSeqImp'{os="kalle",bool=true}], - xexp=[#'XSeqExp'{os="kalle",bool=true}, - #'XSeqExp'{os="kalle",bool=true}]}), + #'SeqTagExpX'{xnt=[#'XSeqNT'{os = <<"kalle">>,bool=true}, + #'XSeqNT'{os = <<"kalle">>,bool=true}], + ximp=[#'XSeqImp'{os = <<"kalle">>,bool=true}, + #'XSeqImp'{os = <<"kalle">>,bool=true}], + xexp=[#'XSeqExp'{os = <<"kalle">>,bool=true}, + #'XSeqExp'{os = <<"kalle">>,bool=true}]}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSeqPrim.erl b/lib/asn1/test/testSeqPrim.erl index eb21d50a37..7f3ef86ac5 100644 --- a/lib/asn1/test/testSeqPrim.erl +++ b/lib/asn1/test/testSeqPrim.erl @@ -25,6 +25,7 @@ -record('Seq',{bool, boolCon, boolPri, boolApp, boolExpCon, boolExpPri, boolExpApp}). -record('Empty',{}). +-record('Big', {os1,os2,os3}). main(_Rules) -> roundtrip('Seq', #'Seq'{bool=true,boolCon=true,boolPri=true,boolApp=true, @@ -35,6 +36,9 @@ main(_Rules) -> roundtrip('Seq', #'Seq'{bool=false,boolCon=true,boolPri=false,boolApp=true, boolExpCon=false,boolExpPri=true,boolExpApp=false}), roundtrip('Empty', #'Empty'{}), + roundtrip('Big', #'Big'{os1=list_to_binary(lists:duplicate(120, 16#A5)), + os2=list_to_binary(lists:duplicate(128, 16#A7)), + os3=list_to_binary(lists:duplicate(17777, 16#F5))}), ok. roundtrip(Type, Value) -> diff --git a/lib/asn1/test/testSeqSetDefaultVal.erl b/lib/asn1/test/testSeqSetDefaultVal.erl index 044099199f..c3d9ce33b7 100644 --- a/lib/asn1/test/testSeqSetDefaultVal.erl +++ b/lib/asn1/test/testSeqSetDefaultVal.erl @@ -36,6 +36,7 @@ c = asn1_DEFAULT, d = asn1_DEFAULT, e = asn1_DEFAULT}). +-record('SeqBS2',{bs = asn1_DEFAULT}). -record('SetBS',{a = asn1_DEFAULT, b = asn1_DEFAULT, c = asn1_DEFAULT, @@ -93,6 +94,13 @@ b = asn1_DEFAULT}). -record('S4_b',{ba = asn1_DEFAULT, bb = asn1_DEFAULT}). +-record('SeqNamedInts', + {i1 = asn1_DEFAULT, + i2 = asn1_DEFAULT}). +-record('S5',{s3 = asn1_DEFAULT, + so = asn1_DEFAULT, + soe = asn1_DEFAULT}). +-record('SOI', {soi = asn1_DEFAULT}). main(ber, []) -> %% Nothing to test because plain BER will only use @@ -105,7 +113,11 @@ main(Rule, Opts) -> case {Rule,Opts} of {ber,[der]} -> - der(); + der(), + case 'Default':legacy_erlang_types() of + false -> der_new_types(); + true -> der_legacy() + end; {_,_} -> ok end, @@ -118,51 +130,51 @@ main(Rule, Opts) -> {#'SeqBS'{}, [{#'SeqBS'.a, - [asn1_DEFAULT, - 2#0110101, + [asn1_DEFAULT, %Always. + <<1:1,0:1,1:1,0:1,1:1,1:1,0:1>>], + [2#0110101, %Legacy only. [1,0,1,0,1,1,0], - {1,<<16#AC>>}, - <<1:1,0:1,1:1,0:1,1:1,1:1,0:1>>]}, + {1,<<16#AC>>}]}, {#'SeqBS'.b, [asn1_DEFAULT, - 2#10100010101, + <<16#A8:8,16#A:4>>], + [2#10100010101, [1,0,1,0,1,0,0,0,1,0,1,0], - {4,<<16#A8,16#A0>>}, - <<16#A8:8,16#A:4>>]}, + {4,<<16#A8,16#A0>>}]}, {#'SeqBS'.c, [asn1_DEFAULT, [second], - [0,1], - {6,<<0:1,1:1,0:6>>}, - <<1:2>>]}, + <<1:2>>], + [[0,1], + {6,<<0:1,1:1,0:6>>}]}, {#'SeqBS'.c, %Zeroes on the right [asn1_DEFAULT, [second], - [0,1,0,0,0], - {4,<<0:1,1:1,0:6>>}, - <<1:2,0:17>>]}, + <<1:2,0:17>>], + [[0,1,0,0,0], + {4,<<0:1,1:1,0:6>>}]}, {#'SeqBS'.d, [asn1_DEFAULT, - 2#1001, + <<2#1001:4>>], + [2#1001, [1,0,0,1], - {4,<<2#1001:4,0:4>>}, - <<2#1001:4>>]}, + {4,<<2#1001:4,0:4>>}]}, {#'SeqBS'.e, [asn1_DEFAULT, - [0,1,0,1,1,0,1,0], - {0,<<2#01011010:8>>}, - <<2#01011010:8>>]}, + <<2#01011010:8>>], + [[0,1,0,1,1,0,1,0], + {0,<<2#01011010:8>>}]}, %% Not EQUAL to DEFAULT. {#'SeqBS'.b, + [<<6:3>>], [[1,1,0], %Not equal to DEFAULT - {5,<<6:3,0:5>>}, - <<6:3>>]} + {5,<<6:3,0:5>>}]} ]}, {#'SeqOS'{}, [{#'SeqOS'.a, [asn1_DEFAULT, - [172]]}]}, + <<172>>]}]}, {#'SeqOI'{}, [{#'SeqOI'.a, @@ -170,15 +182,14 @@ main(Rule, Opts) -> {1,2,14,15}]}, {#'SeqOI'.b, [asn1_DEFAULT, -%% {iso,'member-body',250,3,4}, + %% {iso,'member-body',250,3,4}, {1,2,250,3,4}]}, {#'SeqOI'.c, [asn1_DEFAULT, -%% {iso,standard,8571,2,250,4}, + %% {iso,standard,8571,2,250,4}, {1,0,8571,2,250,4}]}]} ], - io:format("~p\n", [Ts]), - R0 = [[consistency(Rec, Pos, Vs) || {Pos,Vs} <- Fs] || {Rec,Fs} <- Ts], + R0 = [[consistency(Rec, PosVs) || PosVs <- Fs] || {Rec,Fs} <- Ts], case lists:flatten(R0) of [] -> ok; @@ -187,8 +198,20 @@ main(Rule, Opts) -> ?t:fail() end. -consistency(Rec0, Pos, [V|Vs]) -> +legacy_filter({_,_}=Keep) -> + Keep; +legacy_filter({Rec,Standard,Legacy}) -> + case 'Default':legacy_erlang_types() of + false -> + {Rec,Standard}; + true -> + {Rec,Standard++Legacy} + end. + +consistency(Rec0, PosVs) -> + {Pos,[V|Vs]=AllVs} = legacy_filter(PosVs), T = element(1, Rec0), + io:format("~p: ~p\n", [T,AllVs]), Rec = setelement(Pos, Rec0, V), {ok,Enc} = 'Default':encode(T, Rec), {ok,_SmokeTest} = 'Default':decode(T, Enc), @@ -206,7 +229,7 @@ consistency_1([V|Vs], Rec0, Pos, Enc) -> consistency_1([], _, _, _) -> []. der() -> - io:put_chars("Peforming DER-specific tests..."), + io:put_chars("Performing DER-specific tests..."), roundtrip(<<48,0>>, 'SeqInts', #'SeqInts'{a=asn1_DEFAULT,b=asn1_DEFAULT, @@ -227,105 +250,6 @@ der() -> #'SetInts'{a=1,b=-1,c=three,d=1}, #'SetInts'{a=1,b=-1,c=3,d=1}), - - roundtrip(<<48,0>>, - 'SeqBS', - #'SeqBS'{a=2#0110101, - b=2#010100010101, - c=[second], - d=[1,0,0,1]}, - #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, - c=[second], d = <<2#1001:4>>, - e = <<2#01011010:8>>}), - roundtrip(<<48,0>>, - 'SeqBS', - #'SeqBS'{a=[1,0,1,0,1,1,0], - b=[1,0,1,0,1,0,0,0,1,0,1,0], - c={5,<<64>>}, - d=2#1001}, - #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, - c=[second], d = <<2#1001:4>>, - e = <<2#01011010:8>>}), - roundtrip(<<48,3,131,1,0>>, - 'SeqBS', - #'SeqBS'{a=[1,0,1,0,1,1,0], - b=[1,0,1,0,1,0,0,0,1,0,1,0], - c={5,<<64>>}, - d=0}, - #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, - c=[second], d = <<>>, - e = <<2#01011010:8>>}), - roundtrip(<<48,3,131,1,0>>, - 'SeqBS', - #'SeqBS'{a = <<1:1,0:1,1:1,0:1,1:1,1:1,0:1>>, - b = <<1:1,0:1,1:1,0:1,1:1,0:1,0:1,0:1,1:1,0:1,1:1,0:1>>, - c = <<2:3>>, - d=0, - e = <<16#5A:8>>}, - #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, - c=[second], d = <<>>, - e = <<2#01011010:8>>}), - - %% None of the default values are used. - roundtrip(<<48,19,128,2,7,128,129,2,5,64,130,2,5,32,131,1,0,132,2,5,224>>, - 'SeqBS', - #'SeqBS'{a = <<1:1>>, - b = {5,<<64>>}, - c = [third], - d = 0, - e = <<7:3>>}, - #'SeqBS'{a = <<1:1>>, - b = <<2:3>>, - c = [third], - d = <<>>, - e = <<7:3>>}), - - roundtrip(<<49,0>>, - 'SetBS', - #'SetBS'{a=2#0110101, - b=2#010100010101, - c=[second], - d=[1,0,0,1]}, - #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, - c=[second], d = <<2#1001:4>>}), - roundtrip(<<49,0>>, - 'SetBS', - #'SetBS'{a=[1,0,1,0,1,1,0], - b=[1,0,1,0,1,0,0,0,1,0,1,0], - c={5,<<64>>}, - d=9}, - #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, - c=[second], d = <<2#1001:4>>}), - roundtrip(<<49,3,131,1,0>>, - 'SetBS', - #'SetBS'{a=[1,0,1,0,1,1,0], - b=[1,0,1,0,1,0,0,0,1,0,1,0], - c={5,<<64>>}, - d=0}, - #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, - c=[second], d = <<>>}), - roundtrip(<<49,3,131,1,0>>, - 'SetBS', - #'SetBS'{a = <<1:1,0:1,1:1,0:1,1:1,1:1,0:1>>, - b = <<1:1,0:1,1:1,0:1,1:1,0:1,0:1,0:1,1:1,0:1,1:1,0:1>>, - c = <<2:3>>, - d=0}, - #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, - c=[second], d = <<>>}), - - roundtrip(<<48,0>>, 'SeqOS', - #'SeqOS'{a=[172],b=[16#A8,16#A0],c='NULL'}), - roundtrip(<<48,0>>, - 'SeqOS', - #'SeqOS'{a=172,b=43168,c='NULL'}, - #'SeqOS'{a=[172],b=[16#A8,16#A0],c='NULL'}), - - roundtrip(<<49,0>>, 'SetOS', #'SetOS'{a=[172],b=[16#A8,16#A0],c='NULL'}), - roundtrip(<<49,0>>, - 'SetOS', - #'SetOS'{a=172,b=43168,c='NULL'}, - #'SetOS'{a=[172],b=[16#A8,16#A0],c='NULL'}), - roundtrip(<<48,0>>, 'SeqOI', #'SeqOI'{a={1,2,14,15}, @@ -450,6 +374,184 @@ der() -> #'S4'{a=#'S2'{a=1,b=asn1_NOVALUE},b=#'S4_b'{ba=true,bb=0}}, #'S4'{a=#'S2'{a=1,b=asn1_NOVALUE},b=#'S4_b'{ba=true,bb=0}}), + roundtrip(<<48,0>>, + 'SeqBS', + #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<2#1001:4>>, + e = <<2#01011010:8>>}), + roundtrip(<<49,0>>, + 'SetBS', + #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<2#1001:4>>}), + + %% None of the default values are used. + roundtrip(<<48,19,128,2,7,128,129,2,5,64,130,2,5,32,131,1,0,132,2,5,224>>, + 'SeqBS', + #'SeqBS'{a = <<1:1>>, + b = <<2:3>>, + c = [third], + d = <<>>, + e = <<7:3>>}), + roundtrip(<<49,3,131,1,0>>, + 'SetBS', + #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<>>}), + + %% SeqNamedInts + roundtrip(<<48,0>>, + 'SeqNamedInts', + #'SeqNamedInts'{i1=15,i2=31}), + roundtrip(<<48,0>>, + 'SeqNamedInts', + #'SeqNamedInts'{}, + #'SeqNamedInts'{i1=15,i2=31}), + roundtrip(<<48,0>>, + 'SeqNamedInts', + #'SeqNamedInts'{i2=last}, + #'SeqNamedInts'{i1=15,i2=31}), + roundtrip(<<48,3,128,1,0>>, + 'SeqNamedInts', + #'SeqNamedInts'{i1=first,i2=31}, + #'SeqNamedInts'{i1=first,i2=31}), + + %% S5 + roundtrip(<<48,0>>, + 'S5', + #'S5'{s3=#'S3'{a=[11,12,13], + b=[{a,11},{b,true},{c,13}], + c=[1,2,3,4], + d=[#'S2'{a=20,b=true},#'S2'{a=30,b=false}]}, + so=[{0,1,999},{0,1,555}], + soe=[]}), + roundtrip(<<48,0>>, + 'S5', + #'S5'{}, + #'S5'{s3=#'S3'{a=[11,12,13], + b=[{a,11},{b,true},{c,13}], + c=[1,2,3,4], + d=[#'S2'{a=20,b=true},#'S2'{a=30,b=false}]}, + so=[{0,1,999},{0,1,555}], + soe=[]}), + + %% SOI + roundtrip(<<48,0>>, + 'SOI', + #'SOI'{}, + #'SOI'{soi=[{1,2,250,9,55},{1,2,250,3,4}]}), + + %% SeqBS2 + roundtrip(<<48,0>>, + 'SeqBS2', + #'SeqBS2'{bs= <<16#5:3>>}), + roundtrip(<<48,0>>, + 'SeqBS2', + #'SeqBS2'{bs= <<16#5:3,0:4>>}, + #'SeqBS2'{bs= <<16#5:3>>}), + + ok. + +der_new_types() -> + io:put_chars("Performing DER-specific tests with new types..."), + + roundtrip(<<48,0>>, 'SeqOS', + #'SeqOS'{a = <<172>>,b = <<16#A8,16#A0>>,c='NULL'}), + + roundtrip(<<49,0>>, 'SetOS', + #'SetOS'{a = <<172>>,b = <<16#A8,16#A0>>,c='NULL'}), + ok. + +der_legacy() -> + io:put_chars("Performing DER-specific tests with legacy types..."), + + roundtrip(<<48,0>>, 'SeqOS', + #'SeqOS'{a=[172],b=[16#A8,16#A0],c='NULL'}), + roundtrip(<<49,0>>, 'SetOS', + #'SetOS'{a=[172],b=[16#A8,16#A0],c='NULL'}), + + roundtrip(<<48,0>>, + 'SeqBS', + #'SeqBS'{a=2#0110101, + b=2#010100010101, + c=[second], + d=[1,0,0,1]}, + #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<2#1001:4>>, + e = <<2#01011010:8>>}), + roundtrip(<<48,0>>, + 'SeqBS', + #'SeqBS'{a=[1,0,1,0,1,1,0], + b=[1,0,1,0,1,0,0,0,1,0,1,0], + c={5,<<64>>}, + d=2#1001}, + #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<2#1001:4>>, + e = <<2#01011010:8>>}), + roundtrip(<<48,3,131,1,0>>, + 'SeqBS', + #'SeqBS'{a=[1,0,1,0,1,1,0], + b=[1,0,1,0,1,0,0,0,1,0,1,0], + c={5,<<64>>}, + d=0}, + #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<>>, + e = <<2#01011010:8>>}), + roundtrip(<<48,3,131,1,0>>, + 'SeqBS', + #'SeqBS'{a = <<1:1,0:1,1:1,0:1,1:1,1:1,0:1>>, + b = <<1:1,0:1,1:1,0:1,1:1,0:1,0:1,0:1,1:1,0:1,1:1,0:1>>, + c = <<2:3>>, + d=0, + e = <<16#5A:8>>}, + #'SeqBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<>>, + e = <<2#01011010:8>>}), + + %% None of the default values are used. + roundtrip(<<48,19,128,2,7,128,129,2,5,64,130,2,5,32,131,1,0,132,2,5,224>>, + 'SeqBS', + #'SeqBS'{a = <<1:1>>, + b = {5,<<64>>}, + c = [third], + d = 0, + e = <<7:3>>}, + #'SeqBS'{a = <<1:1>>, + b = <<2:3>>, + c = [third], + d = <<>>, + e = <<7:3>>}), + roundtrip(<<49,0>>, + 'SetBS', + #'SetBS'{a=2#0110101, + b=2#010100010101, + c=[second], + d=[1,0,0,1]}, + #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<2#1001:4>>}), + roundtrip(<<49,0>>, + 'SetBS', + #'SetBS'{a=[1,0,1,0,1,1,0], + b=[1,0,1,0,1,0,0,0,1,0,1,0], + c={5,<<64>>}, + d=9}, + #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<2#1001:4>>}), + roundtrip(<<49,3,131,1,0>>, + 'SetBS', + #'SetBS'{a=[1,0,1,0,1,1,0], + b=[1,0,1,0,1,0,0,0,1,0,1,0], + c={5,<<64>>}, + d=0}, + #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<>>}), + roundtrip(<<49,3,131,1,0>>, + 'SetBS', + #'SetBS'{a = <<1:1,0:1,1:1,0:1,1:1,1:1,0:1>>, + b = <<1:1,0:1,1:1,0:1,1:1,0:1,0:1,0:1,1:1,0:1,1:1,0:1>>, + c = <<2:3>>, + d=0}, + #'SetBS'{a = <<2#1010110:7>>, b = <<16#A8A:12>>, + c=[second], d = <<>>}), + ok. roundtrip(Encoded, Type, Value) -> diff --git a/lib/asn1/test/testSeqTag.erl b/lib/asn1/test/testSeqTag.erl index 2f127b3e97..6bacca6808 100644 --- a/lib/asn1/test/testSeqTag.erl +++ b/lib/asn1/test/testSeqTag.erl @@ -35,24 +35,24 @@ -record('Exp',{os, bool}). main(_Rules) -> - roundtrip('SeqTag', #'SeqTag'{nt=#'NT'{os="kalle",bool=true}, - imp=#'Imp'{os="kalle",bool=true}, - exp=#'Exp'{os="kalle",bool=true}}), - roundtrip('SeqTagImp', #'SeqTagImp'{nt=#'NT'{os="kalle",bool=true}, - imp=#'Imp'{os="kalle",bool=true}, - exp=#'Exp'{os="kalle",bool=true}}), - roundtrip('SeqTagExp', #'SeqTagExp'{nt=#'NT'{os="kalle",bool=true}, - imp=#'Imp'{os="kalle",bool=true}, - exp=#'Exp'{os="kalle",bool=true}}), - roundtrip('SeqTagX', #'SeqTagX'{xnt=#'XSeqNT'{os="kalle",bool=true}, - ximp=#'XSeqImp'{os="kalle",bool=true}, - xexp=#'XSeqExp'{os="kalle",bool=true}}), - roundtrip('SeqTagImpX', #'SeqTagImpX'{xnt=#'XSeqNT'{os="kalle",bool=true}, - ximp=#'XSeqImp'{os="kalle",bool=true}, - xexp=#'XSeqExp'{os="kalle",bool=true}}), - roundtrip('SeqTagExpX', #'SeqTagExpX'{xnt=#'XSeqNT'{os="kalle",bool=true}, - ximp=#'XSeqImp'{os="kalle",bool=true}, - xexp=#'XSeqExp'{os="kalle",bool=true}}), + roundtrip('SeqTag', #'SeqTag'{nt=#'NT'{os = <<"kalle">>,bool=true}, + imp=#'Imp'{os = <<"kalle">>,bool=true}, + exp=#'Exp'{os = <<"kalle">>,bool=true}}), + roundtrip('SeqTagImp', #'SeqTagImp'{nt=#'NT'{os = <<"kalle">>,bool=true}, + imp=#'Imp'{os = <<"kalle">>,bool=true}, + exp=#'Exp'{os = <<"kalle">>,bool=true}}), + roundtrip('SeqTagExp', #'SeqTagExp'{nt=#'NT'{os = <<"kalle">>,bool=true}, + imp=#'Imp'{os = <<"kalle">>,bool=true}, + exp=#'Exp'{os = <<"kalle">>,bool=true}}), + roundtrip('SeqTagX', #'SeqTagX'{xnt=#'XSeqNT'{os = <<"kalle">>,bool=true}, + ximp=#'XSeqImp'{os = <<"kalle">>,bool=true}, + xexp=#'XSeqExp'{os = <<"kalle">>,bool=true}}), + roundtrip('SeqTagImpX', #'SeqTagImpX'{xnt=#'XSeqNT'{os = <<"kalle">>,bool=true}, + ximp=#'XSeqImp'{os = <<"kalle">>,bool=true}, + xexp=#'XSeqExp'{os = <<"kalle">>,bool=true}}), + roundtrip('SeqTagExpX', #'SeqTagExpX'{xnt=#'XSeqNT'{os = <<"kalle">>,bool=true}, + ximp=#'XSeqImp'{os = <<"kalle">>,bool=true}, + xexp=#'XSeqExp'{os = <<"kalle">>,bool=true}}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSeqTypeRefCho.erl b/lib/asn1/test/testSeqTypeRefCho.erl index b008bc46b8..1a921c6f38 100644 --- a/lib/asn1/test/testSeqTypeRefCho.erl +++ b/lib/asn1/test/testSeqTypeRefCho.erl @@ -28,10 +28,10 @@ main(_Rules) -> roundtrip('SeqTRcho', - #'SeqTRcho'{'seqCho' = {choOs,"A string 1"}, - 'seqChoE' = {choOs,"A string 3"}, - 'seqCho-E' = {choOs,"A string 7"}, - 'seqChoE-E' = {choOs,"A string 9"}}), + #'SeqTRcho'{'seqCho' = {choOs,<<"A string 1">>}, + 'seqChoE' = {choOs,<<"A string 3">>}, + 'seqCho-E' = {choOs,<<"A string 7">>}, + 'seqChoE-E' = {choOs,<<"A string 9">>}}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSeqTypeRefPrim.erl b/lib/asn1/test/testSeqTypeRefPrim.erl index b63882ae99..d66d1ebcfe 100644 --- a/lib/asn1/test/testSeqTypeRefPrim.erl +++ b/lib/asn1/test/testSeqTypeRefPrim.erl @@ -26,15 +26,15 @@ main(_Rules) -> roundtrip('SeqTR', - #'SeqTR'{'octStr' = "A string 1", - 'octStrI' = "A string 2", - 'octStrE' = "A string 3", - 'octStr-I' = "A string 4", - 'octStrI-I' = "A string 5", - 'octStrE-I' = "A string 6", - 'octStr-E' = "A string 7", - 'octStrI-E' = "A string 8", - 'octStrE-E' = "A string 9"}), + #'SeqTR'{'octStr' = <<"A string 1">>, + 'octStrI' = <<"A string 2">>, + 'octStrE' = <<"A string 3">>, + 'octStr-I' = <<"A string 4">>, + 'octStrI-I' = <<"A string 5">>, + 'octStrE-I' = <<"A string 6">>, + 'octStr-E' = <<"A string 7">>, + 'octStrI-E' = <<"A string 8">>, + 'octStrE-E' = <<"A string 9">>}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSeqTypeRefSeq.erl b/lib/asn1/test/testSeqTypeRefSeq.erl index fc2e0a67c9..3288511f0a 100644 --- a/lib/asn1/test/testSeqTypeRefSeq.erl +++ b/lib/asn1/test/testSeqTypeRefSeq.erl @@ -71,15 +71,15 @@ main(_Rules) -> seqS2=#'SeqSTag_seqS2'{b2=true,i2=22}, seqS3=#'SeqSTag_seqS3'{b3=true,i3=33}}), roundtrip('SeqTRseq', - #'SeqTRseq'{seqSeq=#'SeqSeq'{seqInt=2,seqOs="A1"}, - seqSeqI=#'SeqSeq'{seqInt=2,seqOs="A2"}, - seqSeqE=#'SeqSeq'{seqInt=2,seqOs="A3"}, - 'seqSeq-I'=#'SeqSeqImp'{seqInt=2,seqOs="A4"}, - 'seqSeqI-I'=#'SeqSeqImp'{seqInt=2,seqOs="A5"}, - 'seqSeqE-I'=#'SeqSeqImp'{seqInt=2,seqOs="A6"}, - 'seqSeq-E'=#'SeqSeqExp'{seqInt=2,seqOs="A7"}, - 'seqSeqI-E'=#'SeqSeqExp'{seqInt=2,seqOs="A8"}, - 'seqSeqE-E'=#'SeqSeqExp'{seqInt=2,seqOs="A9"}}), + #'SeqTRseq'{seqSeq=#'SeqSeq'{seqInt=2,seqOs = <<"A1">>}, + seqSeqI=#'SeqSeq'{seqInt=2,seqOs = <<"A2">>}, + seqSeqE=#'SeqSeq'{seqInt=2,seqOs = <<"A3">>}, + 'seqSeq-I'=#'SeqSeqImp'{seqInt=2,seqOs = <<"A4">>}, + 'seqSeqI-I'=#'SeqSeqImp'{seqInt=2,seqOs = <<"A5">>}, + 'seqSeqE-I'=#'SeqSeqImp'{seqInt=2,seqOs = <<"A6">>}, + 'seqSeq-E'=#'SeqSeqExp'{seqInt=2,seqOs = <<"A7">>}, + 'seqSeqI-E'=#'SeqSeqExp'{seqInt=2,seqOs = <<"A8">>}, + 'seqSeqE-E'=#'SeqSeqExp'{seqInt=2,seqOs = <<"A9">>}}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSeqTypeRefSet.erl b/lib/asn1/test/testSeqTypeRefSet.erl index 911a4b7a47..d73423284b 100644 --- a/lib/asn1/test/testSeqTypeRefSet.erl +++ b/lib/asn1/test/testSeqTypeRefSet.erl @@ -32,15 +32,15 @@ main(_Rules) -> roundtrip('SeqTRset', - #'SeqTRset'{seqSet=#'SeqSet'{setInt=2,setOs="A1"}, - seqSetI=#'SeqSet'{setInt=2,setOs="A2"}, - seqSetE=#'SeqSet'{setInt=2,setOs="A3"}, - 'seqSet-I'=#'SeqSetImp'{setInt=2,setOs="A4"}, - 'seqSetI-I'=#'SeqSetImp'{setInt=2,setOs="A5"}, - 'seqSetE-I'=#'SeqSetImp'{setInt=2,setOs="A6"}, - 'seqSet-E'=#'SeqSetExp'{setInt=2,setOs="A7"}, - 'seqSetI-E'=#'SeqSetExp'{setInt=2,setOs="A8"}, - 'seqSetE-E'=#'SeqSetExp'{setInt=2,setOs="A9"}}), + #'SeqTRset'{seqSet=#'SeqSet'{setInt=2,setOs = <<"A1">>}, + seqSetI=#'SeqSet'{setInt=2,setOs = <<"A2">>}, + seqSetE=#'SeqSet'{setInt=2,setOs = <<"A3">>}, + 'seqSet-I'=#'SeqSetImp'{setInt=2,setOs = <<"A4">>}, + 'seqSetI-I'=#'SeqSetImp'{setInt=2,setOs = <<"A5">>}, + 'seqSetE-I'=#'SeqSetImp'{setInt=2,setOs = <<"A6">>}, + 'seqSet-E'=#'SeqSetExp'{setInt=2,setOs = <<"A7">>}, + 'seqSetI-E'=#'SeqSetExp'{setInt=2,setOs = <<"A8">>}, + 'seqSetE-E'=#'SeqSetExp'{setInt=2,setOs = <<"A9">>}}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSetExternal.erl b/lib/asn1/test/testSetExternal.erl index e17d7053aa..626adc5822 100644 --- a/lib/asn1/test/testSetExternal.erl +++ b/lib/asn1/test/testSetExternal.erl @@ -28,15 +28,15 @@ -record('SetXSeq3',{bool, int, seq}). main(_Rules) -> - roundtrip('XNTNT', #'XSetNT'{os="kalle",bool=true}), - roundtrip('XImpNT', #'XSetNT'{os="kalle",bool=true}), - roundtrip('XExpNT', #'XSetNT'{os="kalle",bool=true}), - roundtrip('XNTImp', #'XSetImp'{os="kalle",bool=true}), - roundtrip('XImpImp', #'XSetImp'{os="kalle",bool=true}), - roundtrip('XExpImp', #'XSetImp'{os="kalle",bool=true}), - roundtrip('XNTExp', #'XSetExp'{os="kalle",bool=true}), - roundtrip('XImpExp', #'XSetExp'{os="kalle",bool=true}), - roundtrip('XExpExp', #'XSetExp'{os="kalle",bool=true}), + roundtrip('XNTNT', #'XSetNT'{os = <<"kalle">>,bool=true}), + roundtrip('XImpNT', #'XSetNT'{os = <<"kalle">>,bool=true}), + roundtrip('XExpNT', #'XSetNT'{os = <<"kalle">>,bool=true}), + roundtrip('XNTImp', #'XSetImp'{os = <<"kalle">>,bool=true}), + roundtrip('XImpImp', #'XSetImp'{os = <<"kalle">>,bool=true}), + roundtrip('XExpImp', #'XSetImp'{os = <<"kalle">>,bool=true}), + roundtrip('XNTExp', #'XSetExp'{os = <<"kalle">>,bool=true}), + roundtrip('XImpExp', #'XSetExp'{os = <<"kalle">>,bool=true}), + roundtrip('XExpExp', #'XSetExp'{os = <<"kalle">>,bool=true}), roundtrip('SetXSeq1', #'SetXSeq1'{seq=#'XSeq1'{bool1=true,int1=77, seq1=#'XSeqIn'{boolIn=false,intIn=88}}, bool=true,int=66}), diff --git a/lib/asn1/test/testSetOf.erl b/lib/asn1/test/testSetOf.erl index 54c42c1f21..0f82a14625 100644 --- a/lib/asn1/test/testSetOf.erl +++ b/lib/asn1/test/testSetOf.erl @@ -121,9 +121,9 @@ main(_Rules) -> #'SetIn'{boolIn=false,intIn=125}, #'SetIn'{boolIn=false,intIn=225}]}), - roundtrip('SetOs', ["First","Second","Third"]), - roundtrip('SetOsImp', ["First","Second","Third"]), - roundtrip('SetOsExp', ["First","Second","Third"]), + roundtrip('SetOs', [<<"First">>,<<"Second">>,<<"Third">>]), + roundtrip('SetOsImp', [<<"First">>,<<"Second">>,<<"Third">>]), + roundtrip('SetOsExp', [<<"First">>,<<"Second">>,<<"Third">>]), roundtrip('SetEmp', #'SetEmp'{set1=[#'Empty'{}]}), ok. diff --git a/lib/asn1/test/testSetOfExternal.erl b/lib/asn1/test/testSetOfExternal.erl index a380ba5ac1..cc5fe10710 100644 --- a/lib/asn1/test/testSetOfExternal.erl +++ b/lib/asn1/test/testSetOfExternal.erl @@ -28,24 +28,42 @@ -record('Exp',{os, bool}). main(_Rules) -> - roundtrip('NTNT', [#'NT'{os="kalle",bool=true},#'NT'{os="kalle",bool=true}]), - roundtrip('ImpNT', [#'NT'{os="kalle",bool=true},#'NT'{os="kalle",bool=true}]), - roundtrip('ExpNT', [#'NT'{os="kalle",bool=true},#'NT'{os="kalle",bool=true}]), - roundtrip('NTImp', [#'Imp'{os="kalle",bool=true},#'Imp'{os="kalle",bool=true}]), - roundtrip('ImpImp', [#'Imp'{os="kalle",bool=true},#'Imp'{os="kalle",bool=true}]), - roundtrip('ExpImp', [#'Imp'{os="kalle",bool=true},#'Imp'{os="kalle",bool=true}]), - roundtrip('NTExp', [#'Exp'{os="kalle",bool=true},#'Exp'{os="kalle",bool=true}]), - roundtrip('ImpExp', [#'Exp'{os="kalle",bool=true},#'Exp'{os="kalle",bool=true}]), - roundtrip('ExpExp', [#'Exp'{os="kalle",bool=true},#'Exp'{os="kalle",bool=true}]), - roundtrip('XNTNT', [#'XSetNT'{os="kalle",bool=true},#'XSetNT'{os="kalle",bool=true}]), - roundtrip('XImpNT', [#'XSetNT'{os="kalle",bool=true},#'XSetNT'{os="kalle",bool=true}]), - roundtrip('XExpNT', [#'XSetNT'{os="kalle",bool=true},#'XSetNT'{os="kalle",bool=true}]), - roundtrip('XNTImp', [#'XSetImp'{os="kalle",bool=true},#'XSetImp'{os="kalle",bool=true}]), - roundtrip('XImpImp', [#'XSetImp'{os="kalle",bool=true},#'XSetImp'{os="kalle",bool=true}]), - roundtrip('XExpImp', [#'XSetImp'{os="kalle",bool=true},#'XSetImp'{os="kalle",bool=true}]), - roundtrip('XNTExp', [#'XSetExp'{os="kalle",bool=true},#'XSetExp'{os="kalle",bool=true}]), - roundtrip('XImpExp', [#'XSetExp'{os="kalle",bool=true},#'XSetExp'{os="kalle",bool=true}]), - roundtrip('XExpExp', [#'XSetExp'{os="kalle",bool=true},#'XSetExp'{os="kalle",bool=true}]), + roundtrip('NTNT', [#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]), + roundtrip('ImpNT', [#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]), + roundtrip('ExpNT', [#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]), + roundtrip('NTImp', [#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]), + roundtrip('ImpImp', [#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]), + roundtrip('ExpImp', [#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]), + roundtrip('NTExp', [#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]), + roundtrip('ImpExp', [#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]), + roundtrip('ExpExp', [#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]), + roundtrip('XNTNT', [#'XSetNT'{os = <<"kalle">>,bool=true}, + #'XSetNT'{os = <<"kalle">>,bool=true}]), + roundtrip('XImpNT', [#'XSetNT'{os = <<"kalle">>,bool=true}, + #'XSetNT'{os = <<"kalle">>,bool=true}]), + roundtrip('XExpNT', [#'XSetNT'{os = <<"kalle">>,bool=true}, + #'XSetNT'{os = <<"kalle">>,bool=true}]), + roundtrip('XNTImp', [#'XSetImp'{os = <<"kalle">>,bool=true}, + #'XSetImp'{os = <<"kalle">>,bool=true}]), + roundtrip('XImpImp', [#'XSetImp'{os = <<"kalle">>,bool=true}, + #'XSetImp'{os = <<"kalle">>,bool=true}]), + roundtrip('XExpImp', [#'XSetImp'{os = <<"kalle">>,bool=true}, + #'XSetImp'{os = <<"kalle">>,bool=true}]), + roundtrip('XNTExp', [#'XSetExp'{os = <<"kalle">>,bool=true}, + #'XSetExp'{os = <<"kalle">>,bool=true}]), + roundtrip('XImpExp', [#'XSetExp'{os = <<"kalle">>,bool=true}, + #'XSetExp'{os = <<"kalle">>,bool=true}]), + roundtrip('XExpExp', [#'XSetExp'{os = <<"kalle">>,bool=true}, + #'XSetExp'{os = <<"kalle">>,bool=true}]), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSetOfTag.erl b/lib/asn1/test/testSetOfTag.erl index 81bc467abb..0d656f05a6 100644 --- a/lib/asn1/test/testSetOfTag.erl +++ b/lib/asn1/test/testSetOfTag.erl @@ -42,42 +42,42 @@ -record('Exp',{os, bool}). main(_Rules) -> - roundtrip('SetTagNt', #'SetTagNt'{nt=[#'NT'{os="kalle",bool=true}, - #'NT'{os="kalle",bool=true}]}), - roundtrip('SetTagNtI', #'SetTagNtI'{imp=[#'Imp'{os="kalle",bool=true}, - #'Imp'{os="kalle",bool=true}]}), - roundtrip('SetTagNtE', #'SetTagNtE'{exp=[#'Exp'{os="kalle",bool=true}, - #'Exp'{os="kalle",bool=true}]}), - roundtrip('SetTagI', #'SetTagI'{nt=[#'NT'{os="kalle",bool=true}, - #'NT'{os="kalle",bool=true}]}), - roundtrip('SetTagII', #'SetTagII'{imp=[#'Imp'{os="kalle",bool=true}, - #'Imp'{os="kalle",bool=true}]}), - roundtrip('SetTagIE', #'SetTagIE'{exp=[#'Exp'{os="kalle",bool=true}, - #'Exp'{os="kalle",bool=true}]}), - roundtrip('SetTagE', #'SetTagE'{nt=[#'NT'{os="kalle",bool=true}, - #'NT'{os="kalle",bool=true}]}), - roundtrip('SetTagEI', #'SetTagEI'{imp=[#'Imp'{os="kalle",bool=true}, - #'Imp'{os="kalle",bool=true}]}), - roundtrip('SetTagEE', #'SetTagEE'{exp=[#'Exp'{os="kalle",bool=true}, - #'Exp'{os="kalle",bool=true}]}), - roundtrip('SetTagXNt', #'SetTagXNt'{xnt=[#'XSetNT'{os="kalle",bool=true}, - #'XSetNT'{os="kalle",bool=true}]}), - roundtrip('SetTagXI', #'SetTagXI'{ximp=[#'XSetImp'{os="kalle",bool=true}, - #'XSetImp'{os="kalle",bool=true}]}), - roundtrip('SetTagXE', #'SetTagXE'{xexp=[#'XSetExp'{os="kalle",bool=true}, - #'XSetExp'{os="kalle",bool=true}]}), - roundtrip('SetTagImpX', #'SetTagImpX'{xnt=[#'XSetNT'{os="kalle",bool=true}, - #'XSetNT'{os="kalle",bool=true}], - ximp=[#'XSetImp'{os="kalle",bool=true}, - #'XSetImp'{os="kalle",bool=true}], - xexp=[#'XSetExp'{os="kalle",bool=true}, - #'XSetExp'{os="kalle",bool=true}]}), - roundtrip('SetTagExpX', #'SetTagExpX'{xnt=[#'XSetNT'{os="kalle",bool=true}, - #'XSetNT'{os="kalle",bool=true}], - ximp=[#'XSetImp'{os="kalle",bool=true}, - #'XSetImp'{os="kalle",bool=true}], - xexp=[#'XSetExp'{os="kalle",bool=true}, - #'XSetExp'{os="kalle",bool=true}]}), + roundtrip('SetTagNt', #'SetTagNt'{nt=[#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagNtI', #'SetTagNtI'{imp=[#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagNtE', #'SetTagNtE'{exp=[#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagI', #'SetTagI'{nt=[#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagII', #'SetTagII'{imp=[#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagIE', #'SetTagIE'{exp=[#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagE', #'SetTagE'{nt=[#'NT'{os = <<"kalle">>,bool=true}, + #'NT'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagEI', #'SetTagEI'{imp=[#'Imp'{os = <<"kalle">>,bool=true}, + #'Imp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagEE', #'SetTagEE'{exp=[#'Exp'{os = <<"kalle">>,bool=true}, + #'Exp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagXNt', #'SetTagXNt'{xnt=[#'XSetNT'{os = <<"kalle">>,bool=true}, + #'XSetNT'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagXI', #'SetTagXI'{ximp=[#'XSetImp'{os = <<"kalle">>,bool=true}, + #'XSetImp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagXE', #'SetTagXE'{xexp=[#'XSetExp'{os = <<"kalle">>,bool=true}, + #'XSetExp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagImpX', #'SetTagImpX'{xnt=[#'XSetNT'{os = <<"kalle">>,bool=true}, + #'XSetNT'{os = <<"kalle">>,bool=true}], + ximp=[#'XSetImp'{os = <<"kalle">>,bool=true}, + #'XSetImp'{os = <<"kalle">>,bool=true}], + xexp=[#'XSetExp'{os = <<"kalle">>,bool=true}, + #'XSetExp'{os = <<"kalle">>,bool=true}]}), + roundtrip('SetTagExpX', #'SetTagExpX'{xnt=[#'XSetNT'{os = <<"kalle">>,bool=true}, + #'XSetNT'{os = <<"kalle">>,bool=true}], + ximp=[#'XSetImp'{os = <<"kalle">>,bool=true}, + #'XSetImp'{os = <<"kalle">>,bool=true}], + xexp=[#'XSetExp'{os = <<"kalle">>,bool=true}, + #'XSetExp'{os = <<"kalle">>,bool=true}]}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSetTag.erl b/lib/asn1/test/testSetTag.erl index 5863a149b9..fa1d84f50d 100644 --- a/lib/asn1/test/testSetTag.erl +++ b/lib/asn1/test/testSetTag.erl @@ -34,24 +34,24 @@ -record('Exp',{os, bool}). main(_Rules) -> - roundtrip('SetTag', #'SetTag'{nt=#'NT'{os="kalle",bool=true}, - imp=#'Imp'{os="kalle",bool=true}, - exp=#'Exp'{os="kalle",bool=true}}), - roundtrip('SetTagImp', #'SetTagImp'{nt=#'NT'{os="kalle",bool=true}, - imp=#'Imp'{os="kalle",bool=true}, - exp=#'Exp'{os="kalle",bool=true}}), - roundtrip('SetTagExp', #'SetTagExp'{nt=#'NT'{os="kalle",bool=true}, - imp=#'Imp'{os="kalle",bool=true}, - exp=#'Exp'{os="kalle",bool=true}}), - roundtrip('SetTagX', #'SetTagX'{xnt=#'XSetNT'{os="kalle",bool=true}, - ximp=#'XSetImp'{os="kalle",bool=true}, - xexp=#'XSetExp'{os="kalle",bool=true}}), - roundtrip('SetTagImpX', #'SetTagImpX'{xnt=#'XSetNT'{os="kalle",bool=true}, - ximp=#'XSetImp'{os="kalle",bool=true}, - xexp=#'XSetExp'{os="kalle",bool=true}}), - roundtrip('SetTagExpX', #'SetTagExpX'{xnt=#'XSetNT'{os="kalle",bool=true}, - ximp=#'XSetImp'{os="kalle",bool=true}, - xexp=#'XSetExp'{os="kalle",bool=true}}), + roundtrip('SetTag', #'SetTag'{nt=#'NT'{os = <<"kalle">>,bool=true}, + imp=#'Imp'{os = <<"kalle">>,bool=true}, + exp=#'Exp'{os = <<"kalle">>,bool=true}}), + roundtrip('SetTagImp', #'SetTagImp'{nt=#'NT'{os = <<"kalle">>,bool=true}, + imp=#'Imp'{os = <<"kalle">>,bool=true}, + exp=#'Exp'{os = <<"kalle">>,bool=true}}), + roundtrip('SetTagExp', #'SetTagExp'{nt=#'NT'{os = <<"kalle">>,bool=true}, + imp=#'Imp'{os = <<"kalle">>,bool=true}, + exp=#'Exp'{os = <<"kalle">>,bool=true}}), + roundtrip('SetTagX', #'SetTagX'{xnt=#'XSetNT'{os = <<"kalle">>,bool=true}, + ximp=#'XSetImp'{os = <<"kalle">>,bool=true}, + xexp=#'XSetExp'{os = <<"kalle">>,bool=true}}), + roundtrip('SetTagImpX', #'SetTagImpX'{xnt=#'XSetNT'{os = <<"kalle">>,bool=true}, + ximp=#'XSetImp'{os = <<"kalle">>,bool=true}, + xexp=#'XSetExp'{os = <<"kalle">>,bool=true}}), + roundtrip('SetTagExpX', #'SetTagExpX'{xnt=#'XSetNT'{os = <<"kalle">>,bool=true}, + ximp=#'XSetImp'{os = <<"kalle">>,bool=true}, + xexp=#'XSetExp'{os = <<"kalle">>,bool=true}}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSetTypeRefCho.erl b/lib/asn1/test/testSetTypeRefCho.erl index 8d62f45bfa..97bbd557e0 100644 --- a/lib/asn1/test/testSetTypeRefCho.erl +++ b/lib/asn1/test/testSetTypeRefCho.erl @@ -29,10 +29,10 @@ main(_Rules) -> roundtrip('SetTRcho', - #'SetTRcho'{'setCho' = {choOs,"A string 1"}, - 'setChoE' = {choOs,"A string 3"}, - 'setCho-E' = {choOs,"A string 7"}, - 'setChoE-E' = {choOs,"A string 9"}}), + #'SetTRcho'{'setCho' = {choOs,<<"A string 1">>}, + 'setChoE' = {choOs,<<"A string 3">>}, + 'setCho-E' = {choOs,<<"A string 7">>}, + 'setChoE-E' = {choOs,<<"A string 9">>}}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSetTypeRefPrim.erl b/lib/asn1/test/testSetTypeRefPrim.erl index cc2e157e68..d441fb789d 100644 --- a/lib/asn1/test/testSetTypeRefPrim.erl +++ b/lib/asn1/test/testSetTypeRefPrim.erl @@ -28,15 +28,15 @@ main(_Rules) -> roundtrip('SetTR', - #'SetTR'{'octStr' = "A string 1", - 'octStrI' = "A string 2", - 'octStrE' = "A string 3", - 'octStr-I' = "A string 4", - 'octStrI-I' = "A string 5", - 'octStrE-I' = "A string 6", - 'octStr-E' = "A string 7", - 'octStrI-E' = "A string 8", - 'octStrE-E' = "A string 9"}), + #'SetTR'{'octStr' = <<"A string 1">>, + 'octStrI' = <<"A string 2">>, + 'octStrE' = <<"A string 3">>, + 'octStr-I' = <<"A string 4">>, + 'octStrI-I' = <<"A string 5">>, + 'octStrE-I' = <<"A string 6">>, + 'octStr-E' = <<"A string 7">>, + 'octStrI-E' = <<"A string 8">>, + 'octStrE-E' = <<"A string 9">>}), ok. roundtrip(T, V) -> diff --git a/lib/asn1/test/testSetTypeRefSeq.erl b/lib/asn1/test/testSetTypeRefSeq.erl index 17af5c2922..a2b5f5745e 100644 --- a/lib/asn1/test/testSetTypeRefSeq.erl +++ b/lib/asn1/test/testSetTypeRefSeq.erl @@ -30,23 +30,23 @@ main(_Rules) -> roundtrip('SetTRseq', - #'SetTRseq'{'setSeq' = #'SetSeq'{seqOs = "A1", + #'SetTRseq'{'setSeq' = #'SetSeq'{seqOs = <<"A1">>, seqInt = 2}, - 'setSeqI' = #'SetSeq'{seqOs = "A2", + 'setSeqI' = #'SetSeq'{seqOs = <<"A2">>, seqInt = 2}, - 'setSeqE' = #'SetSeq'{seqOs = "A3", + 'setSeqE' = #'SetSeq'{seqOs = <<"A3">>, seqInt = 2}, - 'setSeq-I' = #'SetSeqImp'{seqOs = "A4", + 'setSeq-I' = #'SetSeqImp'{seqOs = <<"A4">>, seqInt = 2}, - 'setSeqI-I' = #'SetSeqImp'{seqOs = "A5", + 'setSeqI-I' = #'SetSeqImp'{seqOs = <<"A5">>, seqInt = 2}, - 'setSeqE-I' = #'SetSeqImp'{seqOs = "A6", + 'setSeqE-I' = #'SetSeqImp'{seqOs = <<"A6">>, seqInt = 2}, - 'setSeq-E' = #'SetSeqExp'{seqOs = "A7", + 'setSeq-E' = #'SetSeqExp'{seqOs = <<"A7">>, seqInt = 2}, - 'setSeqI-E' = #'SetSeqExp'{seqOs = "A8", + 'setSeqI-E' = #'SetSeqExp'{seqOs = <<"A8">>, seqInt = 2}, - 'setSeqE-E' = #'SetSeqExp'{seqOs = "A9", + 'setSeqE-E' = #'SetSeqExp'{seqOs = <<"A9">>, seqInt = 2}}), ok. diff --git a/lib/asn1/test/testSetTypeRefSet.erl b/lib/asn1/test/testSetTypeRefSet.erl index 8786e0fb4d..80a6be58c9 100644 --- a/lib/asn1/test/testSetTypeRefSet.erl +++ b/lib/asn1/test/testSetTypeRefSet.erl @@ -71,15 +71,15 @@ main(_Rules) -> setS2=#'SetSTag_setS2'{b2=true,i2=22}, setS3=#'SetSTag_setS3'{b3=true,i3=33}}), roundtrip('SetTRset', - #'SetTRset'{setSet=#'SetSet'{setInt=2,setOs="A1"}, - setSetI=#'SetSet'{setInt=2,setOs="A2"}, - setSetE=#'SetSet'{setInt=2,setOs="A3"}, - 'setSet-I'=#'SetSetImp'{setInt=2,setOs="A4"}, - 'setSetI-I'=#'SetSetImp'{setInt=2,setOs="A5"}, - 'setSetE-I'=#'SetSetImp'{setInt=2,setOs="A6"}, - 'setSet-E'=#'SetSetExp'{setInt=2,setOs="A7"}, - 'setSetI-E'=#'SetSetExp'{setInt=2,setOs="A8"}, - 'setSetE-E'=#'SetSetExp'{setInt=2,setOs="A9"}}), + #'SetTRset'{setSet=#'SetSet'{setInt=2,setOs = <<"A1">>}, + setSetI=#'SetSet'{setInt=2,setOs = <<"A2">>}, + setSetE=#'SetSet'{setInt=2,setOs = <<"A3">>}, + 'setSet-I'=#'SetSetImp'{setInt=2,setOs = <<"A4">>}, + 'setSetI-I'=#'SetSetImp'{setInt=2,setOs = <<"A5">>}, + 'setSetE-I'=#'SetSetImp'{setInt=2,setOs = <<"A6">>}, + 'setSet-E'=#'SetSetExp'{setInt=2,setOs = <<"A7">>}, + 'setSetI-E'=#'SetSetExp'{setInt=2,setOs = <<"A8">>}, + 'setSetE-E'=#'SetSetExp'{setInt=2,setOs = <<"A9">>}}), ok. diff --git a/lib/asn1/test/testTcapsystem.erl b/lib/asn1/test/testTcapsystem.erl index 4979a385b2..fcc9e084e0 100644 --- a/lib/asn1/test/testTcapsystem.erl +++ b/lib/asn1/test/testTcapsystem.erl @@ -21,44 +21,42 @@ -export([compile/2]). --include_lib("test_server/include/test_server.hrl"). - compile(Config, Options) -> - [asn1_test_lib:compile(filename:join([tcapsystem, M]), Config, Options) - || M <- ["DialoguePDUs.asn", - "MAP-ApplicationContexts.asn", - "MAP-BS-Code.asn", - "MAP-CallHandlingOperations.asn", - "MAP-CH-DataTypes.asn", - "MAP-CommonDataTypes.asn", - "MAP-DialogueInformation.asn", - "MAP-ER-DataTypes.asn", - "MAP-Errors.asn", - "MAP-ExtensionDataTypes.asn", - "MAP-GR-DataTypes.asn", - "MAP-Group-Call-Operations.asn", - "MAP-LCS-DataTypes.asn", - "MAP-LocationServiceOperations.asn", - "MAP-MobileServiceOperations.asn", - "MAP-MS-DataTypes.asn", - "MAP-OM-DataTypes.asn", - "MAP-OperationAndMaintenanceOperations.asn", - "MAP-Protocol.asn", - "MAP-SecureTransportOperations.asn", - "MAP-ShortMessageServiceOperations.asn", - "MAP-SM-DataTypes.asn", - "MAP-SS-Code.asn", - "MAP-SS-DataTypes.asn", - "MAP-ST-DataTypes.asn", - "MAP-SupplementaryServiceOperations.asn", - "MAP-TS-Code.asn", - "MobileDomainDefinitions.asn", - "Remote-Operations-Generic-ROS-PDUs.asn", - "Remote-Operations-Information-Objects.asn", - "Remote-Operations-Useful-Definitions.asn", - "TCAP-Examples.asn", - "TCAPMessages.asn", - "TCAP-Tools.asn", - "TC-Notation-Extensions.asn", - "UnidialoguePDUs.asn"]], - ok. + Fs = [filename:join("tcapsystem", M) || + M <- ["DialoguePDUs.asn", + "MAP-ApplicationContexts.asn", + "MAP-BS-Code.asn", + "MAP-CallHandlingOperations.asn", + "MAP-CH-DataTypes.asn", + "MAP-CommonDataTypes.asn", + "MAP-DialogueInformation.asn", + "MAP-ER-DataTypes.asn", + "MAP-Errors.asn", + "MAP-ExtensionDataTypes.asn", + "MAP-GR-DataTypes.asn", + "MAP-Group-Call-Operations.asn", + "MAP-LCS-DataTypes.asn", + "MAP-LocationServiceOperations.asn", + "MAP-MobileServiceOperations.asn", + "MAP-MS-DataTypes.asn", + "MAP-OM-DataTypes.asn", + "MAP-OperationAndMaintenanceOperations.asn", + "MAP-Protocol.asn", + "MAP-SecureTransportOperations.asn", + "MAP-ShortMessageServiceOperations.asn", + "MAP-SM-DataTypes.asn", + "MAP-SS-Code.asn", + "MAP-SS-DataTypes.asn", + "MAP-ST-DataTypes.asn", + "MAP-SupplementaryServiceOperations.asn", + "MAP-TS-Code.asn", + "MobileDomainDefinitions.asn", + "Remote-Operations-Generic-ROS-PDUs.asn", + "Remote-Operations-Information-Objects.asn", + "Remote-Operations-Useful-Definitions.asn", + "TCAP-Examples.asn", + "TCAPMessages.asn", + "TCAP-Tools.asn", + "TC-Notation-Extensions.asn", + "UnidialoguePDUs.asn"]], + asn1_test_lib:compile_all(Fs, Config, Options). diff --git a/lib/asn1/test/testTimer.erl b/lib/asn1/test/testTimer.erl index 0f02bab6e0..89bc2b463d 100644 --- a/lib/asn1/test/testTimer.erl +++ b/lib/asn1/test/testTimer.erl @@ -18,163 +18,141 @@ %% %% -module(testTimer). --export([go/2]). +-export([go/0]). -include_lib("test_server/include/test_server.hrl"). -define(times, 5000). val() -> - _Value = {'H323-UserInformation',{'H323-UU-PDU', - {callProceeding, - {'CallProceeding-UUIE', - {0,8,222}, - {'EndpointType', - {'NonStandardParameter', - {object,{0,9,237}}, - "O"}, - {'VendorIdentifier', - {'H221NonStandard',62,63,16282}, - "OC", - "OC"}, - {'GatekeeperInfo', - {'NonStandardParameter', - {object,{0,10,260}}, - "O"}}, - {'GatewayInfo', - [{h320, - {'H320Caps', - {'NonStandardParameter', - {object,{0,11,282}}, - "O"}, - [{'DataRate', - {'NonStandardParameter', - {object, - {0,11,295}}, - "O"}, - 1290470518, - 78}], - [{'SupportedPrefix', - {'NonStandardParameter', - {object, - {0,12,312}}, - "O"}, - {'h323-ID',"BM"}}]}}], - {'NonStandardParameter', - {object,{0,13,326}}, - "O"}}, - {'McuInfo', - {'NonStandardParameter', - {object,{1,13,340,340}}, - "OC"}}, - {'TerminalInfo', - {'NonStandardParameter', - {object,{1,14,353,354}}, - "OC"}}, - true, - true}, - {ipxAddress, - {'TransportAddress_ipxAddress', - "OCTET ", - "OCTE", - "OC"}}, - {'CallIdentifier',"OCTET STRINGOCTE"}, - {noSecurity,'NULL'}, - [{'ClearToken', - 1667517741, - "BM", - {'DHset',[1],[1],[1]}, - "OCTET STR", - -26430296, - {'TypedCertificate', - {1,16,405,406}, - "OC"}, - "BMP", - {'NonStandardParameter', - {1,16,414,415}, - "OC"}}, - {'ClearToken', - 1817656756, - "BMP", - {'DHset',[1],[1],[1]}, - "OCTET STRI", - -16356110, - {'TypedCertificate', - {1,17,442,443}, - "OC"}, - "BMP", - {'NonStandardParameter', - {1,18,452,452}, - "OC"}}], - [{cryptoGKPwdEncr, - {'CryptoH323Token_cryptoGKPwdEncr', - {1,18,467,467}, - {'Params',-7477016,"OCTET ST"}, - "OC"}}, - {cryptoGKPwdEncr, - {'CryptoH323Token_cryptoGKPwdEncr', - {1,19,486,486}, - {'Params',-2404513,"OCTET ST"}, - []}}], - []}}, - {'NonStandardParameter',{object,{0,3,84}},[]}, - [], - true, - [], - []}, - {'H323-UserInformation_user-data',24,"O"}}. + {'H323-UserInformation',{'H323-UU-PDU', + {callProceeding, + {'CallProceeding-UUIE', + {0,8,222}, + {'EndpointType', + {'NonStandardParameter', + {object,{0,9,237}}, + <<"O">>}, + {'VendorIdentifier', + {'H221NonStandard',62,63,16282}, + <<"OC">>, + <<"OC">>}, + {'GatekeeperInfo', + {'NonStandardParameter', + {object,{0,10,260}}, + <<"O">>}}, + {'GatewayInfo', + [{h320, + {'H320Caps', + {'NonStandardParameter', + {object,{0,11,282}}, + <<"O">>}, + [{'DataRate', + {'NonStandardParameter', + {object, + {0,11,295}}, + <<"O">>}, + 1290470518, + 78}], + [{'SupportedPrefix', + {'NonStandardParameter', + {object, + {0,12,312}}, + <<"O">>}, + {'h323-ID',"BM"}}]}}], + {'NonStandardParameter', + {object,{0,13,326}}, + <<"O">>}}, + {'McuInfo', + {'NonStandardParameter', + {object,{1,13,340,340}}, + <<"OC">>}}, + {'TerminalInfo', + {'NonStandardParameter', + {object,{1,14,353,354}}, + <<"OC">>}}, + true, + true}, + {ipxAddress, + {'TransportAddress_ipxAddress', + <<"OCTET ">>, + <<"OCTE">>, + <<"OC">>}}, + {'CallIdentifier',<<"OCTET STRINGOCTE">>}, + {noSecurity,'NULL'}, + [{'ClearToken', + 1667517741, + "BM", + {'DHset',<<1:1>>,<<1:1>>,<<1:1>>}, + <<"OCTET STR">>, + -26430296, + {'TypedCertificate', + {1,16,405,406}, + <<"OC">>}, + "BMP", + {'NonStandardParameter', + {1,16,414,415}, + <<"OC">>}}, + {'ClearToken', + 1817656756, + "BMP", + {'DHset',<<1:1>>,<<1:1>>,<<1:1>>}, + <<"OCTET STRI">>, + -16356110, + {'TypedCertificate', + {1,17,442,443}, + <<"OC">>}, + "BMP", + {'NonStandardParameter', + {1,18,452,452}, + <<"OC">>}}], + [{cryptoGKPwdEncr, + {'CryptoH323Token_cryptoGKPwdEncr', + {1,18,467,467}, + {'Params',-7477016,<<"OCTET ST">>}, + <<"OC">>}}, + {cryptoGKPwdEncr, + {'CryptoH323Token_cryptoGKPwdEncr', + {1,19,486,486}, + {'Params',-2404513,<<"OCTET ST">>}, + <<>>}}], + []}}, + {'NonStandardParameter',{object,{0,3,84}},<<>>}, + [], + true, + [], + []}, + {'H323-UserInformation_user-data',24,<<"O">>}}. -go(Config, _Enc) -> - ?line true = code:add_patha(?config(priv_dir,Config)), - +go() -> Module = 'H323-MESSAGES', Type = 'H323-UserInformation', Value = val(), - {ok,Bytes} = asn1rt:encode(Module,Type,Value), + Bytes = Module:encode(Type, Value), + Value = Module:decode(Type, Bytes), - CompileOptions = compile_options(), - {ValWr,done} = timer:tc(fun() -> encode(?times, Module, Type, Value) end), - ?line io:format("ASN1 encode ~p: ~p micro~n", [CompileOptions, ValWr / ?times]), + io:format("ASN.1 encoding: ~p micro~n", [ValWr / ?times]), done = decode(2, Module, Type, Bytes), {ValRead,done} = timer:tc(fun() -> decode(?times, Module, Type, Bytes) end), - ?line io:format("ASN1 decode ~p: ~p micro~n", [CompileOptions, ValRead /?times]), - + io:format("ASN.1 decoding: ~p micro~n", [ValRead /?times]), - ?line Comment = "encode: "++integer_to_list(round(ValWr/?times))++ - " micro, decode: "++integer_to_list(round(ValRead /?times))++ - " micro. " ++ CompileOptions, + Comment = "encode: "++integer_to_list(round(ValWr/?times)) ++ + " micro, decode: "++integer_to_list(round(ValRead /?times)) ++ + " micro. [" ++ atom_to_list(Module:encoding_rule()) ++ "]", {comment,Comment}. encode(0, _Module,_Type,_Value) -> done; encode(N, Module,Type,Value) -> - ?line {ok,B} = asn1rt:encode(Module,Type,Value), - _B2 = if - is_list(B) -> list_to_binary(B); - true -> B - end, - encode(N-1, Module,Type,Value). + Module:encode(Type, Value), + encode(N-1, Module, Type, Value). decode(0, _Module, _Type, _Value) -> done; decode(N, Module, Type, Value) -> - {ok,_B} = asn1rt:decode(Module, Type, Value), + Module:decode(Type, Value), decode(N-1, Module, Type, Value). - -compile_options() -> - {ok,Info} = asn1rt:info('H323-MESSAGES'), - case lists:keyfind(options, 1, Info) of - {_,Opts0} -> - Opts1 = [X || X <- Opts0, - (X =:= ber orelse - X =:= per orelse - X =:= uper)], - lists:flatten(io_lib:format("~p", [Opts1])); - _ -> - "[]" - end. - diff --git a/lib/asn1/test/testTypeValueNotation.erl b/lib/asn1/test/testTypeValueNotation.erl index b46d7177f5..2b5f3f74c1 100644 --- a/lib/asn1/test/testTypeValueNotation.erl +++ b/lib/asn1/test/testTypeValueNotation.erl @@ -24,7 +24,7 @@ -record('Seq', {octstr, int, bool, enum, bitstr, null, oid, vstr}). main(_Rule, _Option) -> - Value = #'Seq'{octstr = [1, 2, 3, 4], + Value = #'Seq'{octstr = <<1,2,3,4>>, int = 12, bool = true, enum = a, diff --git a/lib/asn1/test/test_compile_options.erl b/lib/asn1/test/test_compile_options.erl index 179299c78d..7f358e863c 100644 --- a/lib/asn1/test/test_compile_options.erl +++ b/lib/asn1/test/test_compile_options.erl @@ -123,7 +123,7 @@ verbose(Config) when is_list(Config) -> ?line ok = asn1ct:compile(Asn1File, [{i,DataDir},{outdir,OutDir},noobj,verbose]), ?line test_server:capture_stop(), ?line [Line0|_] = test_server:capture_get(), - ?line true = lists:prefix("Erlang ASN.1 version", Line0), + ?line true = lists:prefix("Erlang ASN.1 compiler", Line0), %% Test non-verbose compile ?line test_server:capture_start(), diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk index 153c64ebdd..d87c50637d 100644 --- a/lib/asn1/vsn.mk +++ b/lib/asn1/vsn.mk @@ -1,2 +1,2 @@ #next version number to use is 2.0 -ASN1_VSN = 2.0.4 +ASN1_VSN = 3.0.2 |