asn1: Expand decoration w/ C types

This commits allows `heim_object_t` as a type and causes the generated
code to use the `heim_retain()` and `heim_release()` functions for
copying and releasing values of such types.

Also, now one can have more than one decoration per-type.
This commit is contained in:
Nicolas Williams
2022-01-10 18:09:07 -06:00
parent ef906991fd
commit 40d1271094
14 changed files with 335 additions and 276 deletions

View File

@@ -28,6 +28,7 @@ libasn1_la_LIBADD = \
libasn1template_la_LIBADD = \
libasn1base.la \
@LIB_com_err@ \
$(LIB_heimbase) \
$(LIBADD_roken)
BUILT_SOURCES = \
@@ -188,10 +189,12 @@ check_timegm_LDADD = $(check_der_LDADD)
check_gen_template_LDADD = \
libasn1template.la \
$(LIB_heimbase) \
$(LIB_roken)
check_gen_LDADD = \
libasn1template.la \
$(LIB_heimbase) \
$(LIB_roken)
check_ber_LDADD = $(check_gen_LDADD)

View File

@@ -14,7 +14,7 @@ annotations](/lib/asn1/README-X681.md).
5. [Limitations](#Limitations)
6. [Compiler Usage](#Compiler-usage)
7. [APIs Generated by the Compiler](#APIs-generated-by-the-compiler)
8. [asn1_print Usage](#asn1_print-usage)
8. [`asn1_print` Usage](#asn1_print-usage)
9. [Implementation](#implementation)
10. [Moving From C](#moving-from-c)
@@ -166,11 +166,14 @@ In recent times the following features have been added:
- Automatic open type traversal, using a subset of X.681/X.682/X.683 for
expressing the requisite metadata.
- Decoration of ASN.1 types with "hidden" fields (ones that don't get encoded
or decoded) of ASN.1 or C types.
## Futures
- JER support?
- XDR/OER support?
- XDR/NDR/OER support?
- Generate comparators? (lib/hx509 has a half-baked Certificate comparator)
@@ -181,6 +184,10 @@ In recent times the following features have been added:
- Most of X.690 is supported for decoding, with only DER supported for
encoding.
- Dumping of ASN.1 modules as JSON (note: the JSON schema for ASN.1 modules is
not stable yet). This enables analysis using, e.g., `jq` or similar, and
even construction of alternative compilers using `jq` or similar.
- We have an `asn1_print` program that can decode DER from any exported types
from any ASN.1 modules committed in Heimdal:
@@ -781,9 +788,9 @@ In recent times the following features have been added:
We'll fix this eventually.)
- Unconstrained integer types have a large integer representation in C that is
not terribly useful in common cases. Range constraints on integer types
cause the compiler to use `int32_t`, `int64_t`, `uint32_t`, and/or
`uint64_t`.
not terribly useful in common cases. Range and member constraints on
integer types cause the compiler to use `int`, `int64_t`, `unsigned int`,
and/or `uint64_t` as appropriate.
- The Heimdal ASN.1 compiler currently handles a large subset of X.680, and
(in a branch) a small subset of X.681, X.682, and X.683, which manifests as
@@ -800,7 +807,7 @@ In recent times the following features have been added:
JER this will have the side-effect of printing the wrong type names in some
cases because two or more types have the same templates and get deduped.)
- There is an _experimental_ ASN.1 -> JSON feature in the compiler. It
- There is an _experimental_ ASN.1 module -> JSON feature in the compiler. It
currently dumps type and value definitions, but not class, or object set
definitions. Even for types, it is not complete, and the JSON schema used
is subject to change *WITHOUT NOTICE*.
@@ -813,7 +820,8 @@ In recent times the following features have been added:
## Limitations
- `asn1_print`'s JSON support is not X.697 (JER) compatible.
- `libasn1`'s and, therefore, `asn1_print`'s JSON support is not X.697 (JER)
compatible.
- Control over C types generated is very limited, mainly only for integer
types.
@@ -839,6 +847,12 @@ In recent times the following features have been added:
values of `SET`, `SEQUENCE`, `SET OF`, and `SEQUENCE OF`), is not supported.
Values of `CHOICE` types are also not supported.
- There is no way to substitute object sets at run-time. This means that
automatic decoding through open types will spend more CPU cycles than the
application might want, by decoding more types than the application might
care about. The ability to substitute object sets at run-time would require
a change to the APIs generated.
- ...
## Compiler Usage
@@ -871,11 +885,8 @@ SYNOPSIS
asn1_compile [--template] [--prefix-enum] [--enum-prefix=PREFIX]
[--encode-rfc1510-bit-string] [--decode-dce-ber]
[--support-ber] [--preserve-binary=TYPE] [--sequence=TYPE]
[--decorate=TYPE:FTYPE:fname[?]]
[--decorate=TYPE:void:fname:::]
[--decorate=TYPE:FTYPE:fname[?]:[copyfn]:[freefn]:header]
[--one-code-file] [--gen-name=NAME] [--option-file=FILE]
[--original-order] [--no-parse-units]
[--decorate=DECORATION] [--one-code-file] [--gen-name=NAME]
[--option-file=FILE] [--original-order] [--no-parse-units]
[--type-file=C-HEADER-FILE] [--version] [--help]
[FILE.asn1 [NAME]]
@@ -883,19 +894,19 @@ DESCRIPTION
asn1_compile compiles an ASN.1 module into C source code and header
files.
A fairly large subset of ASN.1 as specified in X.680, and the ASN.1 In
formation Object System as specified in X.681, X.682, and X.683 is sup
A fairly large subset of ASN.1 as specified in X.680, and the ASN.1 In-
formation Object System as specified in X.681, X.682, and X.683 is sup-
ported, with support for the Distinguished Encoding Rules (DER), partial
Basic Encoding Rules (BER) support, and experimental JSON support (encod
Basic Encoding Rules (BER) support, and experimental JSON support (encod-
ing only at this time).
See the compiler's README files for details about the C code and inter
See the compiler's README files for details about the C code and inter-
faces it generates.
The Information Object System support includes automatic codec support
for encoding and decoding through “open types” which are also known as
“typed holes”. See RFC 5912 for examples of how to use the ASN.1 Infor
mation Object System via X.681/X.682/X.683 annotations. See the com
“typed holes”. See RFC 5912 for examples of how to use the ASN.1 Infor-
mation Object System via X.681/X.682/X.683 annotations. See the com-
piler's README files for more information on ASN.1 Information Object
System support.
@@ -907,7 +918,7 @@ DESCRIPTION
• enable saving of as-received encodings of specific types
for the purpose of signature validation;
• generate add/remove utility functions for array types;
• decorate generated struct types with fields that are nei
• decorate generated struct types with fields that are nei-
ther encoded nor decoded;
etc.
@@ -934,7 +945,7 @@ DESCRIPTION
• The REAL type is not supported.
• The EmbeddedPDV type is not supported.
• The BMPString type is not supported.
• The IA5String is not properly supported, as it's essen
• The IA5String is not properly supported, as it's essen-
tially treated as a UTF8String with a different tag.
• All supported non-octet strings are treated as like the
UTF8String type.
@@ -956,19 +967,19 @@ DESCRIPTION
The codegen backend generates C code for all functions directly,
with no template interpretation.
The template backend scales better than the codegen backend be
cause as we add support for more encoding rules and more opera
The template backend scales better than the codegen backend be-
cause as we add support for more encoding rules and more opera-
tions (we may add value comparators) the templates stay mostly
the same, thus scaling linearly with size of module. Whereas the
codegen backend scales linear with the product of module size and
number of encoding rules supported.
--prefix-enum
This option should be removed because ENUMERATED types should al
This option should be removed because ENUMERATED types should al-
ways have their labels prefixed.
--enum-prefix=PREFIX
This option should be removed because ENUMERATED types should al
This option should be removed because ENUMERATED types should al-
ways have their labels prefixed.
--encode-rfc1510-bit-string
@@ -1000,54 +1011,48 @@ DESCRIPTION
Generate add/remove functions for the named ASN.1 TYPE which must
be a SET OF or SEQUENCE OF type.
--decorate=TYPE:FTYPE:fname[?]
Add to the C struct generated for the given ASN.1 type named TYPE
a “hidden” field named fname of the given ASN.1 type FTYPE, but
do not encode or decode it. The TYPE must be a SET or SEQUENCE
type. If the fname ends in a question mark, then treat the field
as OPTIONAL.
--decorate=ASN1-TYPE:FIELD-ASN1-TYPE:fname[?]
Add to the C struct generated for the given ASN.1 SET or SEQUENCE
type named ASN1-TYPE a “hidden” field named fname of the given
ASN.1 type FIELD-ASN1-TYPE, but do not encode or decode it. If
the fname ends in a question mark, then treat the field as OP-
TIONAL.
This is useful for adding fields to existing types that can be
used for internal bookkeeping but which do not affect interoper
ability because they are neither encoded nor decoded. For exam
used for internal bookkeeping but which do not affect interoper-
ability because they are neither encoded nor decoded. For exam-
ple, one might decorate a request type with state needed during
processing of the request.
--decorate=TYPE:void:fname:::
Add to the C struct generated for the given ASN.1 type named TYPE
a “hidden” field named fname of type void * (but do not encode
or decode it.
--decorate=ASN1-TYPE:heim_object_t:fname
Add to the C struct generated for the given ASN.1 SET or SEQUENCE
type named ASN1-TYPE a “hidden” field named fname of C type
heim_object_t values of which will be copied and released with
heim_release() and heim_retain() respectively.
--decorate=ASN1-TYPE:void*:fname
Add to the C struct generated for the given ASN.1 SET or SEQUENCE
type named ASN1-TYPE a “hidden” field named fname of type void
* (but do not encode or decode it.
The destructor and copy constructor functions generated by this
compiler for TYPE will set this field to the NULL pointer.
compiler for ASN1-TYPE will set this field to the NULL pointer.
This is useful for adding fields to existing types that can be
used for internal bookkeeping but which do not affect interoper
ability because they are neither encoded nor decoded. For exam
ple, one might decorate a request type with state needed during
processing of the request.
--decorate=TYPE:FTYPE:fname[?]:copyfn:freefn:header
Add to the C struct generated for the given ASN.1 type named TYPE
a “hidden” field named fname of the given external C type FTYPE,
declared in the given header but do not encode or decode this
field. The TYPE must be a SET or SEQUENCE type. If the fname
ends in a question mark, then treat the field as OPTIONAL.
This is useful for adding fields to existing types that can be
used for internal bookkeeping but which do not affect interoper
ability because they are neither encoded nor decoded. For exam
ple, one might decorate a request type with state needed during
processing of the request.
--decorate=ASN1-TYPE:FIELD-C-TYPE:fname[?]:[copyfn]:[freefn]:header
Add to the C struct generated for the given ASN.1 SET or SEQUENCE
type named ASN1-TYPE a “hidden” field named fname of the given
external C type FIELD-C-TYPE, declared in the given header but do
not encode or decode this field. If the fname ends in a question
mark, then treat the field as OPTIONAL.
The header must include double quotes or angle brackets. The
copyfn must be the name of a copy constructor function that takes
a pointer to a source value of the type, and a pointer to a des
a pointer to a source value of the type, and a pointer to a des-
tination value of the type, in that order, and which returns zero
on success or else a system error code on failure. The freefn
must be the name of a destructor function that takes a pointer to
a value of the type and which releases resources referenced by
that value, but does not free the value itself (the run-time al
that value, but does not free the value itself (the run-time al-
locates this value as needed from the C heap). The freefn should
also reset the value to a pristine state (such as all zeros).
@@ -1069,7 +1074,7 @@ DESCRIPTION
--original-order
Attempt to preserve the original order of type definition in the
ASN.1 module. By default the compiler generates types in a topo
ASN.1 module. By default the compiler generates types in a topo-
logical sort order.
--no-parse-units
@@ -1085,9 +1090,7 @@ DESCRIPTION
--help
NOTES
Note that only one decoration per-SET/SEQUENCE type may be specified.
Currently only the template backend supports automatic encoding and de
Currently only the template backend supports automatic encoding and de-
coding of open types via the ASN.1 Information Object System and
X.681/X.682/X.683 annotations.
@@ -1303,13 +1306,19 @@ See:
- Add JER support so we can convert between JER and DER?
- Add XDR support?
- Add XDR support? There are no ASN.1 Encoding Rules based on XDR, but it is
trivial to construct such for at least that subset of ASN.1 for which the
XDR syntax has equivalent semantics.
- Add OER support?
- Add NDR support?
- Add NDR support? There are no ASN.1 Encoding Rules based on NDR, but it is
trivial to construct such for at least that subset of ASN.1 for which the
IDL syntax has equivalent semantics.
- Perhaps third parties will contribute more control over generate types?
- Perhaps third parties will contribute more control over generated types?
This may require separate publication of the Heimdal ASN.1 compiler from the
rest of Heimdal.
## Moving From C

View File

@@ -150,6 +150,7 @@
#define A1_FLAG_OPTIONAL (0x01000000)
#define A1_FLAG_IMPLICIT (0x02000000)
#define A1_FLAG_DEFAULT (0x04000000)
#define A1_FLAG_HEIM_OBJ (0x08000000)
#define A1_TAG_T(CLASS,TYPE,TAG) ((A1_OP_TAG) | (((CLASS) << 22) | ((TYPE) << 21) | (TAG)))
#define A1_TAG_CLASS(x) (((x) >> 22) & 0x3)

View File

@@ -48,9 +48,7 @@
.Op Fl Fl support-ber
.Op Fl Fl preserve-binary=TYPE
.Op Fl Fl sequence=TYPE
.Op Fl Fl decorate=TYPE:FTYPE:fname[?]
.Op Fl Fl decorate=TYPE:void:fname:::
.Op Fl Fl decorate=TYPE:FTYPE:fname[?]:[copyfn]:[freefn]:header
.Op Fl Fl decorate=DECORATION
.Op Fl Fl one-code-file
.Op Fl Fl gen-name=NAME
.Op Fl Fl option-file=FILE
@@ -240,19 +238,17 @@ which must be a
or
.Sq SEQUENCE OF
type.
.It Fl Fl decorate=TYPE:FTYPE:fname[?]
Add to the C struct generated for the given ASN.1 type named
.Ar TYPE
.It Fl Fl decorate=ASN1-TYPE:FIELD-ASN1-TYPE:fname[?]
Add to the C struct generated for the given ASN.1 SET or SEQUENCE type
named
.Ar ASN1-TYPE
a
.Dq hidden
field named
.Ar fname
of the given ASN.1 type
.Ar FTYPE ,
.Ar FIELD-ASN1-TYPE ,
but do not encode or decode it.
The
.Ar TYPE
must be a SET or SEQUENCE type.
If the
.Ar fname
ends in a question mark, then treat the field as OPTIONAL.
@@ -262,9 +258,25 @@ used for internal bookkeeping but which do not affect
interoperability because they are neither encoded nor decoded.
For example, one might decorate a request type with state needed
during processing of the request.
.It Fl Fl decorate=TYPE:void:fname:::
Add to the C struct generated for the given ASN.1 type named
.Ar TYPE
.It Fl Fl decorate=ASN1-TYPE:heim_object_t:fname
Add to the C struct generated for the given ASN.1 SET or SEQUENCE type
named
.Ar ASN1-TYPE
a
.Dq hidden
field named
.Ar fname
of C type
.Sq heim_object_t
values of which will be copied and released with
.Sq heim_retain()
and
.Sq heim_release()
respectively.
.It Fl Fl decorate=ASN1-TYPE:void*:fname
Add to the C struct generated for the given ASN.1 SET or SEQUENCE type
named
.Ar ASN1-TYPE
a
.Dq hidden
field named
@@ -275,41 +287,27 @@ of type
.Pp
The destructor and copy constructor functions generated by this
compiler for
.Ar TYPE
.Ar ASN1-TYPE
will set this field to the
.Sq NULL
pointer.
.Pp
This is useful for adding fields to existing types that can be
used for internal bookkeeping but which do not affect
interoperability because they are neither encoded nor decoded.
For example, one might decorate a request type with state needed
during processing of the request.
.It Fl Fl decorate=TYPE:FTYPE:fname[?]:copyfn:freefn:header
Add to the C struct generated for the given ASN.1 type named
.Ar TYPE
.It Fl Fl decorate=ASN1-TYPE:FIELD-C-TYPE:fname[?]:[copyfn]:[freefn]:header
Add to the C struct generated for the given ASN.1 SET or SEQUENCE type
named
.Ar ASN1-TYPE
a
.Dq hidden
field named
.Ar fname
of the given external C type
.Ar FTYPE ,
.Ar FIELD-C-TYPE ,
declared in the given
.Ar header
but do not encode or decode this field.
The
.Ar TYPE
must be a SET or SEQUENCE type.
If the
.Ar fname
ends in a question mark, then treat the field as OPTIONAL.
.Pp
This is useful for adding fields to existing types that can be
used for internal bookkeeping but which do not affect
interoperability because they are neither encoded nor decoded.
For example, one might decorate a request type with state needed
during processing of the request.
.Pp
The
.Ar header
must include double quotes or angle brackets.
@@ -364,9 +362,6 @@ for common type defintions.
.It Fl Fl help
.El
.Sh NOTES
Note that only one decoration per-SET/SEQUENCE type may be
specified.
.Pp
Currently only the template backend supports automatic encoding
and decoding of open types via the ASN.1 Information Object
System and X.681/X.682/X.683 annotations.

View File

@@ -1039,18 +1039,18 @@ static int
test_decorated(void)
{
TESTNotDecorated tnd;
TESTDecorated3 td3, td3_copy;
TESTDecorated2 td2, td2_copy;
TESTDecorated td, td_copy;
size_t len, size;
void *ptr;
int ret;
memset(&td, 0, sizeof(td));
memset(&td2, 0, sizeof(td2));
memset(&tnd, 0, sizeof(tnd));
td.version = 3;
td.version3.v = 5;
td.privthing = &td;
td.privobj = heim_string_create("foo");
if ((td.version2 = malloc(sizeof(*td.version2))) == NULL)
errx(1, "out of memory");
*td.version2 = 5;
@@ -1085,90 +1085,35 @@ test_decorated(void)
warnx("copy_TESTDecorated() did not work correctly (2)");
return 1;
}
if (td.version3.v != td_copy.version3.v) {
warnx("copy_TESTDecorated() did not work correctly (3)");
return 1;
}
if (td_copy.privthing != 0) {
warnx("copy_TESTDecorated() did not work correctly (4)");
return 1;
}
if (td_copy.privobj != td.privobj) {
warnx("copy_TESTDecorated() did not work correctly (5)");
return 1;
}
free_TESTDecorated(&td_copy);
free_TESTDecorated(&td);
if (td.version2) {
warnx("free_TESTDecorated() did not work correctly");
warnx("free_TESTDecorated() did not work correctly (1)");
return 1;
}
td2.version = 3;
td2.version2.v = 5;
ASN1_MALLOC_ENCODE(TESTDecorated2, ptr, len, &td2, &size, ret);
if (ret) {
warnx("could not encode a TESTDecorated2 struct");
if (td.version3.v != 0 || my_free_vers_called != 2) {
warnx("free_TESTDecorated() did not work correctly (2)");
return 1;
}
ret = decode_TESTNotDecorated(ptr, len, &tnd, &size);
if (ret) {
warnx("could not decode a TESTDecorated2 struct as TESTNotDecorated2");
if (td.privthing != 0) {
warnx("free_TESTDecorated() did not work correctly (3)");
return 1;
}
free(ptr);
if (size != len) {
warnx("TESTDecorated2 encoded size mismatch");
return 1;
}
if (td2.version != tnd.version) {
warnx("TESTDecorated2 did not decode as a TESTNotDecorated correctly");
return 1;
}
if (copy_TESTDecorated2(&td2, &td2_copy)) {
warnx("copy_TESTDecorated2() failed");
return 1;
}
if (td2.version != td2_copy.version || !my_copy_vers_called) {
warnx("copy_TESTDecorated2() did not work correctly (1)");
return 1;
}
if (td2.version2.v != td2_copy.version2.v) {
warnx("copy_TESTDecorated2() did not work correctly (2)");
return 1;
}
free_TESTDecorated2(&td2_copy);
free_TESTDecorated2(&td2);
if (td2.version2.v != 0 || my_free_vers_called != 2) {
warnx("free_TESTDecorated2() did not work correctly");
return 1;
}
td3.version = 3;
td3.privthing = &td;
ASN1_MALLOC_ENCODE(TESTDecorated3, ptr, len, &td3, &size, ret);
if (ret) {
warnx("could not encode a TESTDecorated3 struct");
return 1;
}
ret = decode_TESTNotDecorated(ptr, len, &tnd, &size);
if (ret) {
warnx("could not decode a TESTDecorated3 struct as TESTNotDecorated3");
return 1;
}
free(ptr);
if (size != len) {
warnx("TESTDecorated3 encoded size mismatch");
return 1;
}
if (td3.version != tnd.version) {
warnx("TESTDecorated3 did not decode as a TESTNotDecorated correctly");
return 1;
}
if (copy_TESTDecorated3(&td3, &td3_copy)) {
warnx("copy_TESTDecorated3() failed");
return 1;
}
if (td3.version != td3_copy.version) {
warnx("copy_TESTDecorated3() did not work correctly (1)");
return 1;
}
if (td3_copy.privthing != 0) {
warnx("copy_TESTDecorated3() did not work correctly (2)");
return 1;
}
free_TESTDecorated3(&td3_copy);
free_TESTDecorated3(&td3);
if (td3.privthing != 0) {
warnx("free_TESTDecorated3() did not work correctly");
if (td.privobj != 0) {
warnx("free_TESTDecorated() did not work correctly (4)");
return 1;
}
return 0;

View File

@@ -1366,12 +1366,13 @@ define_type(int level, const char *name, const char *basename, Type *pt, Type *t
case TSequence: {
Member *m;
struct decoration deco;
ssize_t more_deco = -1;
int decorated = 0;
getnewbasename(&newbasename, typedefp || level == 0, basename, name);
space(level);
(void) decorate_type(newbasename, &deco);
fprintf (headerfile, "struct %s {\n", newbasename);
fprintf(jsonfile, "\"ttype\":\"%s\",\"extensible\":%s,"
"\"ctype\":\"struct %s\"",
@@ -1408,19 +1409,34 @@ define_type(int level, const char *name, const char *basename, Type *pt, Type *t
fprintf(jsonfile, ",\"opentype\":");
define_open_type(level, newbasename, name, basename, t, t);
}
if (deco.decorated) {
while (decorate_type(newbasename, &deco, &more_deco)) {
decorated++;
space(level + 1);
fprintf(headerfile, "%s %s%s;\n", deco.field_type,
deco.opt ? "*" : "", deco.field_name);
fprintf(jsonfile, ",\"decorate\":{"
if (deco.first)
fprintf(jsonfile, ",\"decorate\":[");
fprintf(jsonfile, "%s{"
"\"type\":\"%s\",\"name\":\"%s\",\"optional\":%s,"
"\"external\":%s,\"copy_function\":\"%s\","
"\"free_function\":\"%s\",\"header_name\":\"%s\"}",
"\"external\":%s,\"pointer\":%s,\"void_star\":%s,"
"\"struct_star\":%s,\"heim_object\":%s,"
"\"copy_function\":\"%s\","
"\"free_function\":\"%s\",\"header_name\":%s%s%s"
"}",
deco.first ? "" : ",",
deco.field_type, deco.field_name,
deco.opt ? "true" : "false", deco.ext ? "true" : "false",
deco.copy_function_name, deco.free_function_name,
deco.header_name);
deco.ptr ? "true" : "false", deco.void_star ? "true" : "false",
deco.struct_star ? "true" : "false", deco.heim_object ? "true" : "false",
deco.copy_function_name ? deco.copy_function_name : "",
deco.free_function_name ? deco.free_function_name : "",
deco.header_name && deco.header_name[0] == '"' ? "" : "\"",
deco.header_name ? deco.header_name : "",
deco.header_name && deco.header_name[0] == '"' ? "" : "\""
);
}
if (decorated)
fprintf(jsonfile, "]");
space(level);
fprintf (headerfile, "} %s;\n", name);
free(deco.field_type);
@@ -1637,13 +1653,13 @@ declare_type(const Symbol *s, Type *t, int typedefp)
case TSet:
case TSequence: {
struct decoration deco;
ssize_t more_deco = -1;
getnewbasename(&newbasename, TRUE, s->gen_name, s->gen_name);
fprintf(headerfile, "struct %s %s;\n", newbasename, s->gen_name);
if (decorate_type(newbasename, &deco) && deco.header_name
&& !deco.void_star
&& deco.header_name[0]) {
fprintf(headerfile, "#include %s\n", deco.header_name);
while (decorate_type(newbasename, &deco, &more_deco)) {
if (deco.header_name)
fprintf(headerfile, "#include %s\n", deco.header_name);
free(deco.field_type);
}
break;
@@ -1782,6 +1798,7 @@ static void
generate_type_header (const Symbol *s)
{
Type *t = s->type;
if (!s->type)
return;
@@ -1850,6 +1867,7 @@ generate_type_header (const Symbol *s)
if (is_export(s->name))
fprintf(symsfile, "ASN1_SYM_TYPE(\"%s\", \"%s\", %s)\n",
s->name, s->gen_name, s->gen_name);
fprintf(headerfile, "typedef ");
define_type(0, s->gen_name, s->gen_name, NULL, s->type, TRUE,
preserve_type(s->name) ? TRUE : FALSE);

View File

@@ -229,6 +229,7 @@ void
generate_type_copy (const Symbol *s)
{
struct decoration deco;
ssize_t more_deco = -1;
int preserve = preserve_type(s->name) ? TRUE : FALSE;
int save_used_fail = used_fail;
@@ -240,19 +241,20 @@ generate_type_copy (const Symbol *s)
"memset(to, 0, sizeof(*to));\n",
s->gen_name, s->gen_name, s->gen_name);
copy_type ("from", "to", s->type, preserve);
if (decorate_type(s->gen_name, &deco)) {
if (deco.ext &&
(deco.copy_function_name == NULL ||
deco.copy_function_name[0] == '\0')) {
while (decorate_type(s->gen_name, &deco, &more_deco)) {
if (deco.heim_object) {
fprintf(codefile, "(to)->%s = heim_retain((from)->%s);\n",
deco.field_name, deco.field_name);
} else if (deco.ext && deco.copy_function_name == NULL) {
/* Decorated with field of external type but no copy function */
if (deco.opt || deco.void_star)
if (deco.ptr)
fprintf(codefile, "(to)->%s = 0;\n", deco.field_name);
else
fprintf(codefile, "memset(&(to)->%s, 0, sizeof((to)->%s));\n",
deco.field_name, deco.field_name);
} else if (deco.ext) {
/* Decorated with field of external type w/ copy function */
if (deco.opt) {
if (deco.ptr) {
fprintf(codefile, "if (from->%s) {\n", deco.field_name);
fprintf(codefile, "(to)->%s = malloc(sizeof(*(to)->%s));\n",
deco.field_name, deco.field_name);

View File

@@ -179,6 +179,7 @@ void
generate_type_free (const Symbol *s)
{
struct decoration deco;
ssize_t more_deco = -1;
int preserve = preserve_type(s->name) ? TRUE : FALSE;
fprintf (codefile, "void ASN1CALL\n"
@@ -187,12 +188,13 @@ generate_type_free (const Symbol *s)
s->gen_name, s->gen_name);
free_type ("data", s->type, preserve);
if (decorate_type(s->gen_name, &deco)) {
if (deco.ext &&
(deco.free_function_name == NULL ||
deco.free_function_name[0] == '\0')) {
while (decorate_type(s->gen_name, &deco, &more_deco)) {
if (deco.heim_object) {
fprintf(codefile, "heim_retain((data)->%s);\n", deco.field_name);
fprintf(codefile, "(data)->%s = 0;\n", deco.field_name);
} else if (deco.ext && deco.free_function_name == NULL) {
/* Decorated with field of external type but no free function */
if (deco.opt || deco.void_star)
if (deco.ptr)
fprintf(codefile, "(data)->%s = 0;\n", deco.field_name);
else
fprintf(codefile,
@@ -200,7 +202,7 @@ generate_type_free (const Symbol *s)
deco.field_name, deco.field_name);
} else if (deco.ext) {
/* Decorated with field of external type w/ free function */
if (deco.opt) {
if (deco.ptr) {
fprintf(codefile, "if ((data)->%s) {\n", deco.field_name);
fprintf(codefile, "%s((data)->%s);\n",
deco.free_function_name, deco.field_name);

View File

@@ -152,11 +152,15 @@ struct decoration {
char *free_function_name; /* destructor function name */
char *header_name; /* header name */
unsigned int decorated:1;
unsigned int first:1; /* optional */
unsigned int opt:1; /* optional */
unsigned int ext:1; /* external */
unsigned int ptr:1; /* external, pointer */
unsigned int void_star:1; /* external, void * */
unsigned int struct_star:1; /* external, struct foo * */
unsigned int heim_object:1; /* external, heim_object_t */
};
int decorate_type(const char *, struct decoration *);
int decorate_type(const char *, struct decoration *, ssize_t *);
void generate_header_of_codefile(const char *);
void close_codefile(void);

View File

@@ -459,7 +459,7 @@ add_line_pointer(struct templatehead *t,
}
/*
* Add an entry to a template where the pointer firled is a string literal.
* Add an entry to a template where the pointer field is a string literal.
*/
static void
add_line_string(struct templatehead *t,
@@ -1065,6 +1065,7 @@ template_members(struct templatehead *temp,
Field *typeidfield = NULL;
Member *m;
struct decoration deco;
ssize_t more_deco = -1;
size_t i = 0, typeididx = 0, opentypeidx = 0;
int is_array_of_open_type = 0;
@@ -1106,21 +1107,24 @@ template_members(struct templatehead *temp,
typeidfield, opentypefield, opentypemember,
is_array_of_open_type);
if (decorate_type(basetype, &deco)) {
while (decorate_type(basetype, &deco, &more_deco)) {
char *poffset2;
poffset2 = partial_offset(basetype, deco.field_name, 1, isstruct);
if (deco.ext) {
if (deco.ext && deco.heim_object) {
add_line_string(temp, "0", poffset2,
"A1_OP_TYPE_DECORATE_EXTERN |A1_FLAG_HEIM_OBJ");
} else if (deco.ext) {
char *ptr = NULL;
/* Decorated with external C type */
if (asprintf(&ptr, "&asn1_extern_%s_%s",
basetype, deco.field_type) == -1 || ptr == NULL)
basetype, deco.field_name) == -1 || ptr == NULL)
err(1, "out of memory");
add_line_pointer(temp, ptr, poffset2,
"A1_OP_TYPE_DECORATE_EXTERN %s",
deco.opt && !deco.void_star ? "|A1_FLAG_OPTIONAL" : "");
deco.opt ? "|A1_FLAG_OPTIONAL" : "");
free(ptr);
} else
/* Decorated with a templated ASN.1 type */
@@ -1142,6 +1146,7 @@ template_members(struct templatehead *temp,
Field *typeidfield = NULL;
Member *m;
struct decoration deco;
ssize_t more_deco = -1;
size_t i = 0, typeididx = 0, opentypeidx = 0;
int is_array_of_open_type = 0;
@@ -1183,21 +1188,24 @@ template_members(struct templatehead *temp,
typeidfield, opentypefield, opentypemember,
is_array_of_open_type);
if (decorate_type(basetype, &deco)) {
while (decorate_type(basetype, &deco, &more_deco)) {
char *poffset2;
poffset2 = partial_offset(basetype, deco.field_name, 1, isstruct);
if (deco.ext) {
if (deco.ext && deco.heim_object) {
add_line_string(temp, "0", poffset2,
"A1_OP_TYPE_DECORATE_EXTERN |A1_FLAG_HEIM_OBJ");
} else if (deco.ext) {
char *ptr = NULL;
/* Decorated with external C type */
if (asprintf(&ptr, "&asn1_extern_%s_%s",
basetype, deco.field_type) == -1 || ptr == NULL)
basetype, deco.field_name) == -1 || ptr == NULL)
err(1, "out of memory");
add_line_pointer(temp, ptr, poffset2,
"A1_OP_TYPE_DECORATE_EXTERN %s",
deco.opt && !deco.void_star ? "|A1_FLAG_OPTIONAL" : "");
deco.opt ? "|A1_FLAG_OPTIONAL" : "");
free(ptr);
} else
/* Decorated with a templated ASN.1 type */
@@ -1521,28 +1529,36 @@ generate_template(const Symbol *s)
FILE *f = get_code_file();
const char *dupname;
struct decoration deco;
ssize_t more_deco = -1;
if (use_extern(s)) {
gen_extern_stubs(f, s->gen_name);
return;
}
if (decorate_type(s->gen_name, &deco) && deco.ext) {
if (deco.void_star && deco.header_name[0])
while (decorate_type(s->gen_name, &deco, &more_deco)) {
if (!deco.ext)
continue;
if (deco.heim_object)
fprintf(f, "#include <heimbase.h>\n");
else if (deco.void_star && deco.header_name)
fprintf(f, "#include %s\n", deco.header_name);
fprintf(f,
"static const struct asn1_type_func asn1_extern_%s_%s = {\n"
"\t(asn1_type_encode)0,\n"
"\t(asn1_type_decode)0,\n"
"\t(asn1_type_length)0,\n"
"\t(asn1_type_copy)%s,\n"
"\t(asn1_type_release)%s,\n"
"\t(asn1_type_print)0,\n"
"\tsizeof(%s)\n"
"};\n", s->gen_name, deco.field_type,
deco.copy_function_name && deco.copy_function_name[0] ? deco.copy_function_name : "0",
deco.free_function_name && deco.free_function_name[0] ? deco.free_function_name : "0",
deco.void_star ? "void *" : deco.field_type);
if (!deco.heim_object)
fprintf(f,
"static const struct asn1_type_func asn1_extern_%s_%s = {\n"
"\t(asn1_type_encode)0,\n"
"\t(asn1_type_decode)0,\n"
"\t(asn1_type_length)0,\n"
"\t(asn1_type_copy)%s,\n"
"\t(asn1_type_release)%s,\n"
"\t(asn1_type_print)0,\n"
"\tsizeof(%s)\n"
"};\n", s->gen_name, deco.field_name,
deco.copy_function_name && deco.copy_function_name[0] ?
deco.copy_function_name : "0",
deco.free_function_name && deco.free_function_name[0] ?
deco.free_function_name : "0",
deco.void_star ? "void *" : deco.field_type);
free(deco.field_type);
}

View File

@@ -42,38 +42,86 @@ static getarg_strings seq;
static getarg_strings decorate;
static int
strcmp4qsort(const void *ap, const void *bp)
strcmp4mergesort_r(const void *ap, const void *bp, void *d)
{
return strcmp(*(const char **)ap, *(const char **)bp);
const char *a = *(const char **)ap;
const char *b = *(const char **)bp;
char sep = *(const char *)d;
int cmp;
if (sep) {
const char *sepa = strchr(a, sep);
const char *sepb = strchr(b, sep);
size_t alen, blen;
if (sepa == NULL) sepa = a + strlen(a);
if (sepb == NULL) sepb = b + strlen(b);
alen = sepa - a;
blen = sepb - b;
cmp = strncmp(a, b, alen > blen ? alen : blen);
if (cmp == 0)
cmp = alen - blen;
} else
cmp = strcmp(a, b);
if (cmp == 0)
return (uintptr_t)ap - (uintptr_t)bp; /* stable sort */
return cmp;
}
static int
prefix_check(const char *s, const char *p, size_t plen, char sep, int *cmp)
{
if ((*cmp = strncmp(p, s, plen)) == 0 && s[plen] == sep)
return 1;
if (*cmp == 0)
*cmp = 1;
return 0;
}
static ssize_t
bsearch_strings(struct getarg_strings *strs, const char *s,
size_t prefix_len, char sep)
bsearch_strings(struct getarg_strings *strs, const char *p,
char sep, ssize_t *more)
{
ssize_t right = (ssize_t)strs->num_strings - 1;
ssize_t left = 0;
ssize_t plen = 0;
int cmp;
if (sep)
plen = strlen(p);
if (strs->num_strings == 0)
return -1;
if (sep && more && *more > -1) {
/* If *more > -1 we're continuing an iteration */
if (*more > right)
return -1;
if (prefix_check(strs->strings[*more], p, plen, sep, &cmp))
return (*more)++;
(*more)++;
return -1;
}
while (left <= right) {
ssize_t mid = left + (right - left) / 2;
int cmp;
if (prefix_len) {
cmp = strncmp(s, strs->strings[mid], prefix_len);
if (cmp == 0 && sep) {
if (strs->strings[mid][prefix_len] == sep)
return mid;
cmp = strncmp(&sep, &strs->strings[mid][prefix_len], 1);
}
if (sep) {
int cmp2;
while (prefix_check(strs->strings[mid], p, plen, sep, &cmp) &&
mid > 0 &&
prefix_check(strs->strings[mid - 1], p, plen, sep, &cmp2))
mid--;
} else
cmp = strcmp(s, strs->strings[mid]);
if (cmp == 0)
cmp = strcmp(p, strs->strings[mid]);
if (cmp == 0) {
if (more)
*more = mid + 1;
return mid;
}
if (cmp < 0)
right = mid - 1; /* -1 if `s' is smaller than smallest in strs */
right = mid - 1; /* -1 if `p' is smaller than smallest in strs */
else
left = mid + 1;
}
@@ -83,13 +131,13 @@ bsearch_strings(struct getarg_strings *strs, const char *s,
int
preserve_type(const char *p)
{
return bsearch_strings(&preserve, p, 0, '\0') > -1;
return bsearch_strings(&preserve, p, '\0', 0) > -1;
}
int
seq_type(const char *p)
{
return bsearch_strings(&seq, p, 0, '\0') > -1;
return bsearch_strings(&seq, p, '\0', 0) > -1;
}
/*
@@ -126,23 +174,22 @@ split_str(const char *s, char sep, char ***fs)
* functions to copy and free values of that type.
*/
int
decorate_type(const char *p,
struct decoration *deco)
decorate_type(const char *p, struct decoration *deco, ssize_t *more)
{
ssize_t i;
size_t plen = strlen(p);
char **s[7];
char *junk = NULL;
char *cp;
deco->first = *more == -1;
deco->decorated = 0;
deco->field_type = NULL;
if ((i = bsearch_strings(&decorate, p, plen, ':')) == -1)
if ((i = bsearch_strings(&decorate, p, ':', more)) == -1)
return 0;
deco->decorated = 1;
deco->opt = 0;
deco->ext = 0;
deco->opt = deco->ext = deco->ptr = 0;
deco->void_star = deco->struct_star = deco->heim_object = 0;
deco->field_name = deco->copy_function_name = deco->free_function_name =
deco->header_name = NULL;
@@ -153,7 +200,7 @@ decorate_type(const char *p,
s[4] = &deco->header_name;
s[5] = &junk;
s[6] = NULL;
split_str(decorate.strings[i] + plen + 1, ':', s);
split_str(decorate.strings[i] + strlen(p) + 1, ':', s);
if (junk || deco->field_type[0] == '\0' || !deco->field_name ||
deco->field_name[0] == '\0' || deco->field_name[0] == '?') {
@@ -164,10 +211,27 @@ decorate_type(const char *p,
deco->opt = 1;
*cp = '\0';
}
if (deco->copy_function_name)
if (strcmp(deco->field_type, "void*") == 0 ||
strcmp(deco->field_type, "void *") == 0) {
deco->ext = deco->ptr = deco->void_star = 1;
deco->opt = 1;
deco->header_name = NULL;
} else if (strcmp(deco->field_type, "heim_object_t") == 0) {
deco->ptr = deco->heim_object = 1;
deco->header_name = "<heimbase.h>";
} else if (strncmp(deco->field_type, "struct ", sizeof("struct ") - 1) == 0 &&
deco->field_type[strlen(deco->field_type) - 1] == '*')
deco->ptr = deco->struct_star = 1;
if (deco->ptr || deco->copy_function_name)
deco->ext = 1;
if (deco->ext && strcmp(deco->field_type, "void") == 0)
deco->opt = deco->void_star = 1;
if (deco->ext && deco->copy_function_name && !deco->copy_function_name[0])
deco->copy_function_name = NULL;
if (deco->ext && deco->free_function_name && !deco->free_function_name[0])
deco->free_function_name = NULL;
if (deco->header_name && !deco->header_name[0])
deco->header_name = NULL;
if (deco->ptr)
deco->opt = 0;
return 1;
}
@@ -389,14 +453,14 @@ main(int argc, char **argv)
}
if (preserve.num_strings)
qsort(preserve.strings, preserve.num_strings, sizeof(preserve.strings[0]),
(int (*)(const void *, const void *))strcmp4qsort);
mergesort_r(preserve.strings, preserve.num_strings,
sizeof(preserve.strings[0]), strcmp4mergesort_r, "");
if (seq.num_strings)
qsort(seq.strings, seq.num_strings, sizeof(seq.strings),
(int (*)(const void *, const void *))strcmp4qsort);
mergesort_r(seq.strings, seq.num_strings, sizeof(seq.strings),
strcmp4mergesort_r, "");
if (decorate.num_strings)
qsort(decorate.strings, decorate.num_strings, sizeof(decorate.strings[0]),
(int (*)(const void *, const void *))strcmp4qsort);
mergesort_r(decorate.strings, decorate.num_strings,
sizeof(decorate.strings[0]), strcmp4mergesort_r, ":");
init_generate(file, name);

View File

@@ -37,6 +37,7 @@
#include <com_err.h>
#include <vis.h>
#include <vis-extras.h>
#include <heimbase.h>
#ifndef ENOTSUP
/* Very old MSVC CRTs don't have ENOTSUP */
@@ -2278,8 +2279,13 @@ _asn1_free(const struct asn1_template *t, void *data)
const struct asn1_type_func *f = t->ptr;
(f->release)(el);
} else {
/* A1_OP_TYPE_DECORATE_EXTERN */
const struct asn1_type_func *f = t->ptr;
if (f->release)
if (t->tt & A1_FLAG_HEIM_OBJ) {
heim_release(*(void **)el);
*(void **)el = 0;
} else if (f && f->release)
(f->release)(el);
else
memset(el, 0, f->size);
@@ -2910,7 +2916,11 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to)
ret = (f->copy)(fel, tel);
} else {
const struct asn1_type_func *f = t->ptr;
if (f->copy)
/* A1_OP_TYPE_DECORATE_EXTERN */
if (t->tt & A1_FLAG_HEIM_OBJ)
*(heim_object_t *)tel = heim_retain(*(void **)fel);
else if (f->copy)
ret = (f->copy)(fel, tel);
else
memset(tel, 0, f->size);

View File

@@ -288,23 +288,12 @@ TESTExtensible ::= SEQUENCE {
TESTDecorated ::= SEQUENCE {
version TESTuint32
-- gets decorated with an ASN.1 type (see test.opt)
}
TESTDecorated2 ::= SEQUENCE {
version TESTuint32
-- gets decorated with a C type (see test.opt)
}
TESTDecorated3 ::= SEQUENCE {
version TESTuint32
-- gets decorated with a void * field (see test.opt)
-- gets decorated with varius fields (see test.opt)
}
TESTNotDecorated ::= SEQUENCE {
version TESTuint32
-- should have the same encoding as TESTDecorated, TESTDecorated2, and
-- TESTDecorated3
-- should have the same encoding as TESTDecorated
}
END

View File

@@ -1,4 +1,5 @@
--sequence=TESTSeqOf
--decorate=TESTDecorated:TESTuint32:version2?
--decorate=TESTDecorated2:my_vers:version2:my_copy_vers:my_free_vers:"check-gen.h"
--decorate=TESTDecorated3:void:privthing?:::
--decorate=TESTDecorated:my_vers:version3:my_copy_vers:my_free_vers:"check-gen.h"
--decorate=TESTDecorated:void *:privthing
--decorate=TESTDecorated:heim_object_t:privobj