constr_CHOICE.c 32.4 KB
Newer Older
Lev Walkin's avatar
Lev Walkin committed
1
/*
Lev Walkin's avatar
Lev Walkin committed
2
 * Copyright (c) 2003, 2004, 2005, 2006, 2007 Lev Walkin <vlm@lionet.info>.
Lev Walkin's avatar
Lev Walkin committed
3
 * All rights reserved.
Lev Walkin's avatar
Lev Walkin committed
4 5
 * Redistribution and modifications are permitted subject to BSD license.
 */
Lev Walkin's avatar
Lev Walkin committed
6
#include <asn_internal.h>
Lev Walkin's avatar
Lev Walkin committed
7
#include <constr_CHOICE.h>
8
#include <per_opentype.h>
Lev Walkin's avatar
Lev Walkin committed
9 10 11 12 13 14

/*
 * Number of bytes left for this structure.
 * (ctx->left) indicates the number of bytes _transferred_ for the structure.
 * (size) contains the number of bytes in the buffer passed.
 */
Lev Walkin's avatar
Lev Walkin committed
15
#define	LEFT	((size<(size_t)ctx->left)?size:(size_t)ctx->left)
Lev Walkin's avatar
Lev Walkin committed
16 17 18 19 20 21 22 23 24 25 26 27

/*
 * If the subprocessor function returns with an indication that it wants
 * more data, it may well be a fatal decoding problem, because the
 * size is constrained by the <TLV>'s L, even if the buffer size allows
 * reading more data.
 * For example, consider the buffer containing the following TLVs:
 * <T:5><L:1><V> <T:6>...
 * The TLV length clearly indicates that one byte is expected in V, but
 * if the V processor returns with "want more data" even if the buffer
 * contains way more data than the V processor have seen.
 */
28
#define	SIZE_VIOLATION	(ctx->left >= 0 && (size_t)ctx->left <= size)
Lev Walkin's avatar
Lev Walkin committed
29 30 31 32 33

/*
 * This macro "eats" the part of the buffer which is definitely "consumed",
 * i.e. was correctly converted into local representation or rightfully skipped.
 */
Lev Walkin's avatar
Lev Walkin committed
34
#undef	ADVANCE
Lev Walkin's avatar
Lev Walkin committed
35 36
#define	ADVANCE(num_bytes)	do {		\
		size_t num = num_bytes;		\
Lev Walkin's avatar
Lev Walkin committed
37
		ptr = ((const char *)ptr) + num;\
Lev Walkin's avatar
Lev Walkin committed
38 39 40 41 42 43 44 45 46
		size -= num;			\
		if(ctx->left >= 0)		\
			ctx->left -= num;	\
		consumed_myself += num;		\
	} while(0)

/*
 * Switch to the next phase of parsing.
 */
Lev Walkin's avatar
Lev Walkin committed
47
#undef	NEXT_PHASE
Lev Walkin's avatar
Lev Walkin committed
48 49 50 51 52 53 54 55
#define	NEXT_PHASE(ctx)	do {			\
		ctx->phase++;			\
		ctx->step = 0;			\
	} while(0)

/*
 * Return a standardized complex structure.
 */
Lev Walkin's avatar
Lev Walkin committed
56
#undef	RETURN
Lev Walkin's avatar
Lev Walkin committed
57 58 59 60 61 62 63 64 65
#define	RETURN(_code)	do {			\
		rval.code = _code;		\
		rval.consumed = consumed_myself;\
		return rval;			\
	} while(0)

/*
 * See the definitions.
 */
66 67 68 69
static unsigned _fetch_present_idx(const void *struct_ptr, unsigned off,
                                   unsigned size);
static void _set_present_idx(void *sptr, unsigned offset, unsigned size,
                             unsigned pres);
70 71
static const void *_get_member_ptr(const asn_TYPE_descriptor_t *,
                                   const void *sptr, asn_TYPE_member_t **elm,
Lev Walkin's avatar
Lev Walkin committed
72
                                   unsigned *present);
Lev Walkin's avatar
Lev Walkin committed
73 74 75 76 77 78

/*
 * Tags are canonically sorted in the tag to member table.
 */
static int
_search4tag(const void *ap, const void *bp) {
Lev Walkin's avatar
Lev Walkin committed
79 80
	const asn_TYPE_tag2member_t *a = (const asn_TYPE_tag2member_t *)ap;
	const asn_TYPE_tag2member_t *b = (const asn_TYPE_tag2member_t *)bp;
Lev Walkin's avatar
Lev Walkin committed
81

Lev Walkin's avatar
Lev Walkin committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	int a_class = BER_TAG_CLASS(a->el_tag);
	int b_class = BER_TAG_CLASS(b->el_tag);

	if(a_class == b_class) {
		ber_tlv_tag_t a_value = BER_TAG_VALUE(a->el_tag);
		ber_tlv_tag_t b_value = BER_TAG_VALUE(b->el_tag);

		if(a_value == b_value)
			return 0;
		else if(a_value < b_value)
			return -1;
		else
			return 1;
	} else if(a_class < b_class) {
		return -1;
	} else {
		return 1;
	}
}

/*
 * The decoder of the CHOICE type.
 */
Lev Walkin's avatar
Lev Walkin committed
105
asn_dec_rval_t
Lev Walkin's avatar
Lev Walkin committed
106
CHOICE_decode_ber(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
Lev Walkin's avatar
Lev Walkin committed
107
	void **struct_ptr, const void *ptr, size_t size, int tag_mode) {
Lev Walkin's avatar
Lev Walkin committed
108 109 110
	/*
	 * Bring closer parts of structure description.
	 */
Lev Walkin's avatar
Lev Walkin committed
111 112
	asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
	asn_TYPE_member_t *elements = td->elements;
Lev Walkin's avatar
Lev Walkin committed
113 114 115 116 117

	/*
	 * Parts of the structure being constructed.
	 */
	void *st = *struct_ptr;	/* Target structure. */
Lev Walkin's avatar
Lev Walkin committed
118
	asn_struct_ctx_t *ctx;	/* Decoder context */
Lev Walkin's avatar
Lev Walkin committed
119 120 121

	ber_tlv_tag_t tlv_tag;	/* T from TLV */
	ssize_t tag_len;	/* Length of TLV's T */
Lev Walkin's avatar
Lev Walkin committed
122
	asn_dec_rval_t rval;	/* Return code from subparsers */
Lev Walkin's avatar
Lev Walkin committed
123 124 125

	ssize_t consumed_myself = 0;	/* Consumed bytes from ptr */

Lev Walkin's avatar
Lev Walkin committed
126
	ASN_DEBUG("Decoding %s as CHOICE", td->name);
Lev Walkin's avatar
Lev Walkin committed
127

Lev Walkin's avatar
Lev Walkin committed
128 129 130 131 132 133 134 135 136 137 138 139 140
	/*
	 * Create the target structure if it is not present already.
	 */
	if(st == 0) {
		st = *struct_ptr = CALLOC(1, specs->struct_size);
		if(st == 0) {
			RETURN(RC_FAIL);
		}
	}

	/*
	 * Restore parsing context.
	 */
Lev Walkin's avatar
Lev Walkin committed
141
	ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset);
Lev Walkin's avatar
Lev Walkin committed
142 143 144 145 146 147 148 149 150 151 152 153
	
	/*
	 * Start to parse where left previously
	 */
	switch(ctx->phase) {
	case 0:
		/*
		 * PHASE 0.
		 * Check that the set of tags associated with given structure
		 * perfectly fits our expectations.
		 */

Lev Walkin's avatar
Lev Walkin committed
154
		if(tag_mode || td->tags_count) {
Lev Walkin's avatar
Lev Walkin committed
155
			rval = ber_check_tags(opt_codec_ctx, td, ctx, ptr, size,
156
				tag_mode, -1, &ctx->left, 0);
Lev Walkin's avatar
Lev Walkin committed
157 158
			if(rval.code != RC_OK) {
				ASN_DEBUG("%s tagging check failed: %d",
Lev Walkin's avatar
Lev Walkin committed
159
					td->name, rval.code);
Lev Walkin's avatar
Lev Walkin committed
160
				return rval;
Lev Walkin's avatar
Lev Walkin committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
			}

			if(ctx->left >= 0) {
				/* ?Substracted below! */
				ctx->left += rval.consumed;
			}
			ADVANCE(rval.consumed);
		} else {
			ctx->left = -1;
		}

		NEXT_PHASE(ctx);

		ASN_DEBUG("Structure consumes %ld bytes, buffer %ld",
			(long)ctx->left, (long)size);

		/* Fall through */
	case 1:
		/*
		 * Fetch the T from TLV.
		 */
		tag_len = ber_fetch_tag(ptr, LEFT, &tlv_tag);
Lev Walkin's avatar
Lev Walkin committed
183
		ASN_DEBUG("In %s CHOICE tag length %d", td->name, (int)tag_len);
Lev Walkin's avatar
Lev Walkin committed
184 185 186 187 188 189 190
		switch(tag_len) {
		case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
			/* Fall through */
		case -1: RETURN(RC_FAIL);
		}

		do {
191
			const asn_TYPE_tag2member_t *t2m;
Lev Walkin's avatar
Lev Walkin committed
192
			asn_TYPE_tag2member_t key;
Lev Walkin's avatar
Lev Walkin committed
193 194

			key.el_tag = tlv_tag;
195
			t2m = (const asn_TYPE_tag2member_t *)bsearch(&key,
Lev Walkin's avatar
Lev Walkin committed
196
					specs->tag2el, specs->tag2el_count,
Lev Walkin's avatar
Lev Walkin committed
197 198 199 200 201 202 203 204
					sizeof(specs->tag2el[0]), _search4tag);
			if(t2m) {
				/*
				 * Found the element corresponding to the tag.
				 */
				NEXT_PHASE(ctx);
				ctx->step = t2m->el_no;
				break;
Lev Walkin's avatar
Lev Walkin committed
205
			} else if(specs->ext_start == -1) {
Lev Walkin's avatar
Lev Walkin committed
206 207
				ASN_DEBUG("Unexpected tag %s "
					"in non-extensible CHOICE %s",
Lev Walkin's avatar
Lev Walkin committed
208
					ber_tlv_tag_string(tlv_tag), td->name);
Lev Walkin's avatar
Lev Walkin committed
209 210 211 212 213 214 215 216
				RETURN(RC_FAIL);
			} else {
				/* Skip this tag */
				ssize_t skip;

				ASN_DEBUG("Skipping unknown tag %s",
					ber_tlv_tag_string(tlv_tag));

217
				skip = ber_skip_length(opt_codec_ctx,
Lev Walkin's avatar
Lev Walkin committed
218
					BER_TLV_CONSTRUCTED(ptr),
Lev Walkin's avatar
Lev Walkin committed
219
					(const char *)ptr + tag_len,
Lev Walkin's avatar
Lev Walkin committed
220
					LEFT - tag_len);
Lev Walkin's avatar
Lev Walkin committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238

				switch(skip) {
				case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
					/* Fall through */
				case -1: RETURN(RC_FAIL);
				}

				ADVANCE(skip + tag_len);
				RETURN(RC_OK);
			}
		} while(0);

	case 2:
		/*
		 * PHASE 2.
		 * Read in the element.
		 */
	    do {
Lev Walkin's avatar
Lev Walkin committed
239
		asn_TYPE_member_t *elm;/* CHOICE's element */
Lev Walkin's avatar
Lev Walkin committed
240
		void *memb_ptr;		/* Pointer to the member */
Lev Walkin's avatar
Lev Walkin committed
241
		void **memb_ptr2;	/* Pointer to that pointer */
Lev Walkin's avatar
Lev Walkin committed
242 243 244 245 246 247 248 249

		elm = &elements[ctx->step];

		/*
		 * Compute the position of the member inside a structure,
		 * and also a type of containment (it may be contained
		 * as pointer or using inline inclusion).
		 */
250 251
		if(elm->flags & ATF_POINTER) {
			/* Member is a pointer to another structure */
Lev Walkin's avatar
Lev Walkin committed
252
			memb_ptr2 = (void **)((char *)st + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
253 254 255 256 257 258 259 260
		} else {
			/*
			 * A pointer to a pointer
			 * holding the start of the structure
			 */
			memb_ptr = (char *)st + elm->memb_offset;
			memb_ptr2 = &memb_ptr;
		}
Lev Walkin's avatar
Lev Walkin committed
261 262 263
		/* Set presence to be able to free it properly at any time */
		_set_present_idx(st, specs->pres_offset,
				specs->pres_size, ctx->step + 1);
Lev Walkin's avatar
Lev Walkin committed
264 265 266
		/*
		 * Invoke the member fetch routine according to member's type
		 */
267
		rval = elm->type->op->ber_decoder(opt_codec_ctx, elm->type,
Lev Walkin's avatar
Lev Walkin committed
268
				memb_ptr2, ptr, LEFT, elm->tag_mode);
Lev Walkin's avatar
Lev Walkin committed
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
		switch(rval.code) {
		case RC_OK:
			break;
		case RC_WMORE: /* More data expected */
			if(!SIZE_VIOLATION) {
				ADVANCE(rval.consumed);
				RETURN(RC_WMORE);
			}
			RETURN(RC_FAIL);
		case RC_FAIL: /* Fatal error */
			RETURN(rval.code);
		} /* switch(rval) */
		
		ADVANCE(rval.consumed);
	  } while(0);

		NEXT_PHASE(ctx);

		/* Fall through */
	case 3:
		ASN_DEBUG("CHOICE %s Leftover: %ld, size = %ld, tm=%d, tc=%d",
Lev Walkin's avatar
Lev Walkin committed
290 291
			td->name, (long)ctx->left, (long)size,
			tag_mode, td->tags_count);
Lev Walkin's avatar
Lev Walkin committed
292 293 294 295 296 297 298 299 300 301

		if(ctx->left > 0) {
			/*
			 * The type must be fully decoded
			 * by the CHOICE member-specific decoder.
			 */
			RETURN(RC_FAIL);
		}

		if(ctx->left == -1
Lev Walkin's avatar
Lev Walkin committed
302
		&& !(tag_mode || td->tags_count)) {
Lev Walkin's avatar
Lev Walkin committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
			/*
			 * This is an untagged CHOICE.
			 * It doesn't contain nothing
			 * except for the member itself, including all its tags.
			 * The decoding is completed.
			 */
			NEXT_PHASE(ctx);
			break;
		}

		/*
		 * Read in the "end of data chunks"'s.
		 */
		while(ctx->left < 0) {
			ssize_t tl;

			tl = ber_fetch_tag(ptr, LEFT, &tlv_tag);
			switch(tl) {
			case 0: if(!SIZE_VIOLATION) RETURN(RC_WMORE);
				/* Fall through */
			case -1: RETURN(RC_FAIL);
			}

			/*
			 * Expected <0><0>...
			 */
Lev Walkin's avatar
Lev Walkin committed
329
			if(((const uint8_t *)ptr)[0] == 0) {
Lev Walkin's avatar
Lev Walkin committed
330 331 332 333 334
				if(LEFT < 2) {
					if(SIZE_VIOLATION)
						RETURN(RC_FAIL);
					else
						RETURN(RC_WMORE);
Lev Walkin's avatar
Lev Walkin committed
335
				} else if(((const uint8_t *)ptr)[1] == 0) {
Lev Walkin's avatar
Lev Walkin committed
336 337 338
					/*
					 * Correctly finished with <0><0>.
					 */
Lev Walkin's avatar
Lev Walkin committed
339 340
					ADVANCE(2);
					ctx->left++;
Lev Walkin's avatar
Lev Walkin committed
341 342 343 344
					continue;
				}
			} else {
				ASN_DEBUG("Unexpected continuation in %s",
Lev Walkin's avatar
Lev Walkin committed
345
					td->name);
Lev Walkin's avatar
Lev Walkin committed
346 347 348
				RETURN(RC_FAIL);
			}

Lev Walkin's avatar
Lev Walkin committed
349
			/* UNREACHABLE */
Lev Walkin's avatar
Lev Walkin committed
350 351 352 353 354 355 356 357 358 359 360
		}

		NEXT_PHASE(ctx);
	case 4:
		/* No meaningful work here */
		break;
	}
	
	RETURN(RC_OK);
}

Lev Walkin's avatar
Lev Walkin committed
361
asn_enc_rval_t
362
CHOICE_encode_der(asn_TYPE_descriptor_t *td, void *sptr,
Lev Walkin's avatar
Lev Walkin committed
363 364
		int tag_mode, ber_tlv_tag_t tag,
		asn_app_consume_bytes_f *cb, void *app_key) {
Lev Walkin's avatar
Lev Walkin committed
365 366
	asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
	asn_TYPE_member_t *elm;	/* CHOICE element */
Lev Walkin's avatar
Lev Walkin committed
367
	asn_enc_rval_t erval;
Lev Walkin's avatar
Lev Walkin committed
368 369
	void *memb_ptr;
	size_t computed_size = 0;
Lev Walkin's avatar
Lev Walkin committed
370
	unsigned present;
Lev Walkin's avatar
Lev Walkin committed
371

372
	if(!sptr) ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
373

Lev Walkin's avatar
Lev Walkin committed
374
	ASN_DEBUG("%s %s as CHOICE",
Lev Walkin's avatar
Lev Walkin committed
375
		cb?"Encoding":"Estimating", td->name);
Lev Walkin's avatar
Lev Walkin committed
376

377
	present = _fetch_present_idx(sptr,
Lev Walkin's avatar
Lev Walkin committed
378 379 380 381 382 383
		specs->pres_offset, specs->pres_size);

	/*
	 * If the structure was not initialized, it cannot be encoded:
	 * can't deduce what to encode in the choice type.
	 */
Lev Walkin's avatar
Lev Walkin committed
384
	if(present == 0 || present > td->elements_count) {
Lev Walkin's avatar
Lev Walkin committed
385
		if(present == 0 && td->elements_count == 0) {
Lev Walkin's avatar
Lev Walkin committed
386 387
			/* The CHOICE is empty?! */
			erval.encoded = 0;
388
			ASN__ENCODED_OK(erval);
Lev Walkin's avatar
Lev Walkin committed
389
		}
390
		ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
391 392 393 394 395
	}

	/*
	 * Seek over the present member of the structure.
	 */
Lev Walkin's avatar
Lev Walkin committed
396
	elm = &td->elements[present-1];
397
	if(elm->flags & ATF_POINTER) {
Lev Walkin's avatar
Lev Walkin committed
398
		memb_ptr = *(void **)((char *)sptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
399
		if(memb_ptr == 0) {
400 401
			if(elm->optional) {
				erval.encoded = 0;
402
				ASN__ENCODED_OK(erval);
403
			}
404
			/* Mandatory element absent */
405
			ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
406 407
		}
	} else {
Lev Walkin's avatar
Lev Walkin committed
408
		memb_ptr = (void *)((char *)sptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
409 410 411 412 413 414 415
	}

	/*
	 * If the CHOICE itself is tagged EXPLICIT:
	 * T ::= [2] EXPLICIT CHOICE { ... }
	 * Then emit the appropriate tags.
	 */
Lev Walkin's avatar
Lev Walkin committed
416
	if(tag_mode == 1 || td->tags_count) {
Lev Walkin's avatar
Lev Walkin committed
417 418 419 420 421 422
		/*
		 * For this, we need to pre-compute the member.
		 */
		ssize_t ret;

		/* Encode member with its tag */
423
		erval = elm->type->op->der_encoder(elm->type, memb_ptr,
Lev Walkin's avatar
Lev Walkin committed
424 425 426 427 428
			elm->tag_mode, elm->tag, 0, 0);
		if(erval.encoded == -1)
			return erval;

		/* Encode CHOICE with parent or my own tag */
429
		ret = der_write_tags(td, erval.encoded, tag_mode, 1, tag,
Lev Walkin's avatar
Lev Walkin committed
430
			cb, app_key);
431
		if(ret == -1)
432
			ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
433 434 435 436 437 438
		computed_size += ret;
	}

	/*
	 * Encode the single underlying member.
	 */
439
	erval = elm->type->op->der_encoder(elm->type, memb_ptr,
Lev Walkin's avatar
Lev Walkin committed
440 441 442 443 444 445 446 447 448 449 450 451 452
		elm->tag_mode, elm->tag, cb, app_key);
	if(erval.encoded == -1)
		return erval;

	ASN_DEBUG("Encoded CHOICE member in %ld bytes (+%ld)",
		(long)erval.encoded, (long)computed_size);

	erval.encoded += computed_size;

	return erval;
}

ber_tlv_tag_t
453
CHOICE_outmost_tag(const asn_TYPE_descriptor_t *td, const void *ptr, int tag_mode, ber_tlv_tag_t tag) {
Lev Walkin's avatar
Lev Walkin committed
454
	asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
Lev Walkin's avatar
Lev Walkin committed
455
	unsigned present;
Lev Walkin's avatar
Lev Walkin committed
456

Lev Walkin's avatar
Lev Walkin committed
457 458
	assert(tag_mode == 0); (void)tag_mode;
	assert(tag == 0); (void)tag;
Lev Walkin's avatar
Lev Walkin committed
459 460 461 462 463 464

	/*
	 * Figure out which CHOICE element is encoded.
	 */
	present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size);

Lev Walkin's avatar
Lev Walkin committed
465
	if(present > 0 && present <= td->elements_count) {
466
		const asn_TYPE_member_t *elm = &td->elements[present-1];
Lev Walkin's avatar
Lev Walkin committed
467
		const void *memb_ptr;
Lev Walkin's avatar
Lev Walkin committed
468

469
		if(elm->flags & ATF_POINTER) {
Lev Walkin's avatar
Lev Walkin committed
470
			memb_ptr = *(const void * const *)
Lev Walkin's avatar
Lev Walkin committed
471
					((const char *)ptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
472
		} else {
Lev Walkin's avatar
Lev Walkin committed
473
			memb_ptr = (const void *)
Lev Walkin's avatar
Lev Walkin committed
474
					((const char *)ptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
475 476
		}

Lev Walkin's avatar
Lev Walkin committed
477
		return asn_TYPE_outmost_tag(elm->type, memb_ptr,
Lev Walkin's avatar
Lev Walkin committed
478 479
			elm->tag_mode, elm->tag);
	} else {
Lev Walkin's avatar
Lev Walkin committed
480
		return (ber_tlv_tag_t)-1;
Lev Walkin's avatar
Lev Walkin committed
481 482 483 484
	}
}

int
Lev Walkin's avatar
Lev Walkin committed
485
CHOICE_constraint(asn_TYPE_descriptor_t *td, const void *sptr,
Lev Walkin's avatar
Lev Walkin committed
486
		asn_app_constraint_failed_f *ctfailcb, void *app_key) {
Lev Walkin's avatar
Lev Walkin committed
487
	asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
Lev Walkin's avatar
Lev Walkin committed
488
	unsigned present;
Lev Walkin's avatar
Lev Walkin committed
489 490

	if(!sptr) {
491
		ASN__CTFAIL(app_key, td, sptr,
492 493
			"%s: value not given (%s:%d)",
			td->name, __FILE__, __LINE__);
Lev Walkin's avatar
Lev Walkin committed
494 495 496 497 498 499 500
		return -1;
	}

	/*
	 * Figure out which CHOICE element is encoded.
	 */
	present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);
Lev Walkin's avatar
Lev Walkin committed
501
	if(present > 0 && present <= td->elements_count) {
Lev Walkin's avatar
Lev Walkin committed
502
		asn_TYPE_member_t *elm = &td->elements[present-1];
Lev Walkin's avatar
Lev Walkin committed
503 504
		const void *memb_ptr;

505
		if(elm->flags & ATF_POINTER) {
Lev Walkin's avatar
Lev Walkin committed
506
			memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset);
507 508 509
			if(!memb_ptr) {
				if(elm->optional)
					return 0;
510
				ASN__CTFAIL(app_key, td, sptr,
511 512 513 514
					"%s: mandatory CHOICE element %s absent (%s:%d)",
					td->name, elm->name, __FILE__, __LINE__);
				return -1;
			}
Lev Walkin's avatar
Lev Walkin committed
515
		} else {
Lev Walkin's avatar
Lev Walkin committed
516
			memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
517 518
		}

Lev Walkin's avatar
Lev Walkin committed
519 520
		if(elm->memb_constraints) {
			return elm->memb_constraints(elm->type, memb_ptr,
Lev Walkin's avatar
Lev Walkin committed
521
				ctfailcb, app_key);
Lev Walkin's avatar
Lev Walkin committed
522 523
		} else {
			int ret = elm->type->check_constraints(elm->type,
Lev Walkin's avatar
Lev Walkin committed
524
					memb_ptr, ctfailcb, app_key);
Lev Walkin's avatar
Lev Walkin committed
525 526 527 528 529 530 531
			/*
			 * Cannot inherit it eralier:
			 * need to make sure we get the updated version.
			 */
			elm->memb_constraints = elm->type->check_constraints;
			return ret;
		}
Lev Walkin's avatar
Lev Walkin committed
532
	} else {
533
		ASN__CTFAIL(app_key, td, sptr,
Lev Walkin's avatar
Lev Walkin committed
534
			"%s: no CHOICE element given (%s:%d)",
535
			td->name, __FILE__, __LINE__);
Lev Walkin's avatar
Lev Walkin committed
536 537 538 539
		return -1;
	}
}

Lev Walkin's avatar
Lev Walkin committed
540 541 542
#undef	XER_ADVANCE
#define	XER_ADVANCE(num_bytes)	do {			\
		size_t num = num_bytes;			\
Lev Walkin's avatar
Lev Walkin committed
543
		buf_ptr = (const void *)(((const char *)buf_ptr) + num); \
Lev Walkin's avatar
Lev Walkin committed
544 545 546 547
		size -= num;				\
		consumed_myself += num;			\
	} while(0)

Lev Walkin's avatar
Lev Walkin committed
548 549 550
/*
 * Decode the XER (XML) data.
 */
Lev Walkin's avatar
Lev Walkin committed
551
asn_dec_rval_t
Lev Walkin's avatar
Lev Walkin committed
552
CHOICE_decode_xer(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
Lev Walkin's avatar
Lev Walkin committed
553
	void **struct_ptr, const char *opt_mname,
Lev Walkin's avatar
Lev Walkin committed
554
		const void *buf_ptr, size_t size) {
Lev Walkin's avatar
Lev Walkin committed
555 556 557 558 559 560 561 562 563 564 565 566
	/*
	 * Bring closer parts of structure description.
	 */
	asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
	const char *xml_tag = opt_mname ? opt_mname : td->xml_tag;

	/*
	 * Parts of the structure being constructed.
	 */
	void *st = *struct_ptr;	/* Target structure. */
	asn_struct_ctx_t *ctx;	/* Decoder context */

Lev Walkin's avatar
Lev Walkin committed
567
	asn_dec_rval_t rval;		/* Return value of a decoder */
Lev Walkin's avatar
Lev Walkin committed
568
	ssize_t consumed_myself = 0;	/* Consumed bytes from ptr */
Lev Walkin's avatar
Lev Walkin committed
569
	size_t edx;			/* Element index */
Lev Walkin's avatar
Lev Walkin committed
570 571 572 573 574 575 576 577 578 579 580 581

	/*
	 * Create the target structure if it is not present already.
	 */
	if(st == 0) {
		st = *struct_ptr = CALLOC(1, specs->struct_size);
		if(st == 0) RETURN(RC_FAIL);
	}

	/*
	 * Restore parsing context.
	 */
Lev Walkin's avatar
Lev Walkin committed
582
	ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset);
583 584
	if(ctx->phase == 0 && !*xml_tag)
		ctx->phase = 1;	/* Skip the outer tag checking phase */
Lev Walkin's avatar
Lev Walkin committed
585 586 587 588 589 590

	/*
	 * Phases of XER/XML processing:
	 * Phase 0: Check that the opening tag matches our expectations.
	 * Phase 1: Processing body and reacting on closing tag.
	 * Phase 2: Processing inner type.
Lev Walkin's avatar
Lev Walkin committed
591 592 593
	 * Phase 3: Only waiting for closing tag.
	 * Phase 4: Skipping unknown extensions.
	 * Phase 5: PHASED OUT
Lev Walkin's avatar
Lev Walkin committed
594
	 */
Lev Walkin's avatar
Lev Walkin committed
595
	for(edx = ctx->step; ctx->phase <= 4;) {
Lev Walkin's avatar
Lev Walkin committed
596 597 598 599 600 601 602 603 604 605 606 607
		pxer_chunk_type_e ch_type;	/* XER chunk type */
		ssize_t ch_size;		/* Chunk size */
		xer_check_tag_e tcv;		/* Tag check value */
		asn_TYPE_member_t *elm;

		/*
		 * Go inside the member.
		 */
		if(ctx->phase == 2) {
			asn_dec_rval_t tmprval;
			void *memb_ptr;		/* Pointer to the member */
			void **memb_ptr2;	/* Pointer to that pointer */
608
			unsigned old_present;
Lev Walkin's avatar
Lev Walkin committed
609

Lev Walkin's avatar
Lev Walkin committed
610 611
			elm = &td->elements[edx];

Lev Walkin's avatar
Lev Walkin committed
612 613
			if(elm->flags & ATF_POINTER) {
				/* Member is a pointer to another structure */
Lev Walkin's avatar
Lev Walkin committed
614
				memb_ptr2 = (void **)((char *)st
Lev Walkin's avatar
Lev Walkin committed
615 616 617 618 619 620
					+ elm->memb_offset);
			} else {
				memb_ptr = (char *)st + elm->memb_offset;
				memb_ptr2 = &memb_ptr;
			}

Lev Walkin's avatar
Lev Walkin committed
621
			/* Start/Continue decoding the inner member */
622
			tmprval = elm->type->op->xer_decoder(opt_codec_ctx,
Lev Walkin's avatar
Lev Walkin committed
623 624 625
					elm->type, memb_ptr2, elm->name,
					buf_ptr, size);
			XER_ADVANCE(tmprval.consumed);
Lev Walkin's avatar
Lev Walkin committed
626 627
			ASN_DEBUG("XER/CHOICE: itdf: [%s] code=%d",
				elm->type->name, tmprval.code);
628 629 630
			old_present = _fetch_present_idx(st,
				specs->pres_offset, specs->pres_size);
			assert(old_present == 0 || old_present == edx + 1);
Lev Walkin's avatar
Lev Walkin committed
631 632 633
			/* Record what we've got */
			_set_present_idx(st,
				specs->pres_offset, specs->pres_size, edx + 1);
634 635
			if(tmprval.code != RC_OK)
				RETURN(tmprval.code);
Lev Walkin's avatar
Lev Walkin committed
636 637 638 639
			ctx->phase = 3;
			/* Fall through */
		}

640 641 642 643 644 645
		/* No need to wait for closing tag; special mode. */
		if(ctx->phase == 3 && !*xml_tag) {
			ctx->phase = 5;	/* Phase out */
			RETURN(RC_OK);
		}

Lev Walkin's avatar
Lev Walkin committed
646 647 648
		/*
		 * Get the next part of the XML stream.
		 */
Lev Walkin's avatar
Lev Walkin committed
649
		ch_size = xer_next_token(&ctx->context, buf_ptr, size, &ch_type);
Lev Walkin's avatar
Lev Walkin committed
650 651 652
		if(ch_size == -1) {
            RETURN(RC_FAIL);
        } else {
Lev Walkin's avatar
Lev Walkin committed
653
			switch(ch_type) {
Lev Walkin's avatar
Lev Walkin committed
654 655
			case PXER_WMORE:
                RETURN(RC_WMORE);
Lev Walkin's avatar
Lev Walkin committed
656 657 658 659 660 661 662 663 664
			case PXER_COMMENT:	/* Got XML comment */
			case PXER_TEXT:		/* Ignore free-standing text */
				XER_ADVANCE(ch_size);	/* Skip silently */
				continue;
			case PXER_TAG:
				break;	/* Check the rest down there */
			}
		}

Lev Walkin's avatar
Lev Walkin committed
665
		tcv = xer_check_tag(buf_ptr, ch_size, xml_tag);
666
		ASN_DEBUG("XER/CHOICE checked [%c%c%c%c] vs [%s], tcv=%d",
Lev Walkin's avatar
Lev Walkin committed
667 668 669 670
			ch_size>0?((const uint8_t *)buf_ptr)[0]:'?',
			ch_size>1?((const uint8_t *)buf_ptr)[1]:'?',
			ch_size>2?((const uint8_t *)buf_ptr)[2]:'?',
			ch_size>3?((const uint8_t *)buf_ptr)[3]:'?',
671
		xml_tag, tcv);
Lev Walkin's avatar
Lev Walkin committed
672 673 674 675 676 677 678 679 680 681 682

		/* Skip the extensions section */
		if(ctx->phase == 4) {
			ASN_DEBUG("skip_unknown(%d, %ld)",
				tcv, (long)ctx->left);
			switch(xer_skip_unknown(tcv, &ctx->left)) {
			case -1:
				ctx->phase = 5;
				RETURN(RC_FAIL);
			case 1:
				ctx->phase = 3;
Lev Walkin's avatar
Lev Walkin committed
683
				/* Fall through */
Lev Walkin's avatar
Lev Walkin committed
684 685 686 687 688 689 690 691 692
			case 0:
				XER_ADVANCE(ch_size);
				continue;
			case 2:
				ctx->phase = 3;
				break;
			}
		}

Lev Walkin's avatar
Lev Walkin committed
693 694
		switch(tcv) {
		case XCT_BOTH:
Lev Walkin's avatar
Lev Walkin committed
695
			break;	/* No CHOICE? */
Lev Walkin's avatar
Lev Walkin committed
696
		case XCT_CLOSING:
Lev Walkin's avatar
Lev Walkin committed
697
			if(ctx->phase != 3)
Lev Walkin's avatar
Lev Walkin committed
698 699
				break;
			XER_ADVANCE(ch_size);
Lev Walkin's avatar
Lev Walkin committed
700
			ctx->phase = 5;	/* Phase out */
Lev Walkin's avatar
Lev Walkin committed
701 702 703 704 705 706 707 708
			RETURN(RC_OK);
		case XCT_OPENING:
			if(ctx->phase == 0) {
				XER_ADVANCE(ch_size);
				ctx->phase = 1;	/* Processing body phase */
				continue;
			}
			/* Fall through */
Lev Walkin's avatar
Lev Walkin committed
709 710
		case XCT_UNKNOWN_OP:
		case XCT_UNKNOWN_BO:
Lev Walkin's avatar
Lev Walkin committed
711 712 713 714 715

			if(ctx->phase != 1)
				break;	/* Really unexpected */

			/*
Lev Walkin's avatar
Lev Walkin committed
716
			 * Search which inner member corresponds to this tag.
Lev Walkin's avatar
Lev Walkin committed
717 718 719
			 */
			for(edx = 0; edx < td->elements_count; edx++) {
				elm = &td->elements[edx];
Lev Walkin's avatar
Lev Walkin committed
720
				tcv = xer_check_tag(buf_ptr,ch_size,elm->name);
Lev Walkin's avatar
Lev Walkin committed
721 722 723 724 725 726 727 728 729
				switch(tcv) {
				case XCT_BOTH:
				case XCT_OPENING:
					/*
					 * Process this member.
					 */
					ctx->step = edx;
					ctx->phase = 2;
					break;
Lev Walkin's avatar
Lev Walkin committed
730 731
				case XCT_UNKNOWN_OP:
				case XCT_UNKNOWN_BO:
Lev Walkin's avatar
Lev Walkin committed
732
					continue;
Lev Walkin's avatar
Lev Walkin committed
733 734 735 736 737 738
				default:
					edx = td->elements_count;
					break;	/* Phase out */
				}
				break;
			}
Lev Walkin's avatar
Lev Walkin committed
739 740
			if(edx != td->elements_count)
				continue;
Lev Walkin's avatar
Lev Walkin committed
741 742

			/* It is expected extension */
Lev Walkin's avatar
Lev Walkin committed
743
			if(specs->ext_start != -1) {
Lev Walkin's avatar
Lev Walkin committed
744
				ASN_DEBUG("Got anticipated extension");
Lev Walkin's avatar
Lev Walkin committed
745
				/*
Lev Walkin's avatar
Lev Walkin committed
746 747 748
				 * Check for (XCT_BOTH or XCT_UNKNOWN_BO)
				 * By using a mask. Only record a pure
				 * <opening> tags.
Lev Walkin's avatar
Lev Walkin committed
749
				 */
Lev Walkin's avatar
Lev Walkin committed
750 751 752 753 754 755 756 757 758
				if(tcv & XCT_CLOSING) {
					/* Found </extension> without body */
					ctx->phase = 3; /* Terminating */
				} else {
					ctx->left = 1;
					ctx->phase = 4; /* Skip ...'s */
				}
				XER_ADVANCE(ch_size);
				continue;
Lev Walkin's avatar
Lev Walkin committed
759 760
			}

Lev Walkin's avatar
Lev Walkin committed
761
			/* Fall through */
Lev Walkin's avatar
Lev Walkin committed
762 763 764 765
		default:
			break;
		}

Lev Walkin's avatar
Lev Walkin committed
766 767 768 769 770 771 772
		ASN_DEBUG("Unexpected XML tag [%c%c%c%c] in CHOICE [%s]"
			" (ph=%d, tag=%s)",
			ch_size>0?((const uint8_t *)buf_ptr)[0]:'?',
			ch_size>1?((const uint8_t *)buf_ptr)[1]:'?',
			ch_size>2?((const uint8_t *)buf_ptr)[2]:'?',
			ch_size>3?((const uint8_t *)buf_ptr)[3]:'?',
			td->name, ctx->phase, xml_tag);
Lev Walkin's avatar
Lev Walkin committed
773 774 775
		break;
	}

Lev Walkin's avatar
Lev Walkin committed
776
	ctx->phase = 5;	/* Phase out, just in case */
Lev Walkin's avatar
Lev Walkin committed
777 778 779 780
	RETURN(RC_FAIL);
}


Lev Walkin's avatar
Lev Walkin committed
781
asn_enc_rval_t
Lev Walkin's avatar
Lev Walkin committed
782
CHOICE_encode_xer(asn_TYPE_descriptor_t *td, void *sptr,
Lev Walkin's avatar
Lev Walkin committed
783 784
	int ilevel, enum xer_encoder_flags_e flags,
		asn_app_consume_bytes_f *cb, void *app_key) {
Lev Walkin's avatar
Lev Walkin committed
785
	asn_CHOICE_specifics_t *specs=(asn_CHOICE_specifics_t *)td->specifics;
Lev Walkin's avatar
Lev Walkin committed
786
	asn_enc_rval_t er;
Lev Walkin's avatar
Lev Walkin committed
787
	unsigned present;
Lev Walkin's avatar
Lev Walkin committed
788 789

	if(!sptr)
790
		ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
791 792 793 794 795 796

	/*
	 * Figure out which CHOICE element is encoded.
	 */
	present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);

Lev Walkin's avatar
Lev Walkin committed
797
	if(present == 0 || present > td->elements_count) {
798
		ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
799 800
	}  else {
		asn_enc_rval_t tmper;
Lev Walkin's avatar
Lev Walkin committed
801
		asn_TYPE_member_t *elm = &td->elements[present-1];
Lev Walkin's avatar
Lev Walkin committed
802 803 804 805 806
		void *memb_ptr;
		const char *mname = elm->name;
		unsigned int mlen = strlen(mname);

		if(elm->flags & ATF_POINTER) {
Lev Walkin's avatar
Lev Walkin committed
807
			memb_ptr = *(void **)((char *)sptr + elm->memb_offset);
808
			if(!memb_ptr) ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
809
		} else {
Lev Walkin's avatar
Lev Walkin committed
810
			memb_ptr = (void *)((char *)sptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
811 812 813 814
		}

		er.encoded = 0;

815
		if(!(flags & XER_F_CANONICAL)) ASN__TEXT_INDENT(1, ilevel);
816
		ASN__CALLBACK3("<", 1, mname, mlen, ">", 1);
Lev Walkin's avatar
Lev Walkin committed
817

818
		tmper = elm->type->op->xer_encoder(elm->type, memb_ptr,
Lev Walkin's avatar
Lev Walkin committed
819 820
				ilevel + 1, flags, cb, app_key);
		if(tmper.encoded == -1) return tmper;
821
		er.encoded += tmper.encoded;
Lev Walkin's avatar
Lev Walkin committed
822

823
		ASN__CALLBACK3("</", 2, mname, mlen, ">", 1);
Lev Walkin's avatar
Lev Walkin committed
824 825
	}

826
	if(!(flags & XER_F_CANONICAL)) ASN__TEXT_INDENT(1, ilevel - 1);
Lev Walkin's avatar
Lev Walkin committed
827

828
	ASN__ENCODED_OK(er);
Lev Walkin's avatar
Lev Walkin committed
829
cb_failed:
830
	ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
831 832
}

Lev Walkin's avatar
Lev Walkin committed
833
asn_dec_rval_t
Lev Walkin's avatar
Lev Walkin committed
834
CHOICE_decode_uper(const asn_codec_ctx_t *opt_codec_ctx, asn_TYPE_descriptor_t *td,
Lev Walkin's avatar
Lev Walkin committed
835 836 837
                   const asn_per_constraints_t *constraints, void **sptr,
                   asn_per_data_t *pd) {
    asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
Lev Walkin's avatar
Lev Walkin committed
838
	asn_dec_rval_t rv;
Lev Walkin's avatar
Lev Walkin committed
839
	const asn_per_constraint_t *ct;
Lev Walkin's avatar
Lev Walkin committed
840 841 842 843 844 845
	asn_TYPE_member_t *elm;	/* CHOICE's element */
	void *memb_ptr;
	void **memb_ptr2;
	void *st = *sptr;
	int value;

846 847
	if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx))
		ASN__DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
848

Lev Walkin's avatar
Lev Walkin committed
849 850 851 852 853
	/*
	 * Create the target structure if it is not present already.
	 */
	if(!st) {
		st = *sptr = CALLOC(1, specs->struct_size);
854
		if(!st) ASN__DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
855 856 857 858 859 860 861 862
	}

	if(constraints) ct = &constraints->value;
	else if(td->per_constraints) ct = &td->per_constraints->value;
	else ct = 0;

	if(ct && ct->flags & APC_EXTENSIBLE) {
		value = per_get_few_bits(pd, 1);
863
		if(value < 0) ASN__DECODE_STARVED;
Lev Walkin's avatar
Lev Walkin committed
864 865 866 867 868
		if(value) ct = 0;	/* Not restricted */
	}

	if(ct && ct->range_bits >= 0) {
		value = per_get_few_bits(pd, ct->range_bits);
869
		if(value < 0) ASN__DECODE_STARVED;
Lev Walkin's avatar
Lev Walkin committed
870 871
		ASN_DEBUG("CHOICE %s got index %d in range %d",
			td->name, value, ct->range_bits);
Lev Walkin's avatar
Lev Walkin committed
872
		if(value > ct->upper_bound)
873
			ASN__DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
874 875
	} else {
		if(specs->ext_start == -1)
876
			ASN__DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
877
		value = uper_get_nsnnwn(pd);
878
		if(value < 0) ASN__DECODE_STARVED;
Lev Walkin's avatar
Lev Walkin committed
879
		value += specs->ext_start;
Lev Walkin's avatar
Lev Walkin committed
880
		if((unsigned)value >= td->elements_count)
881
			ASN__DECODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
882 883 884 885 886 887 888 889 890 891 892 893
	}

	/* Adjust if canonical order is different from natural order */
	if(specs->canonical_order)
		value = specs->canonical_order[value];

	/* Set presence to be able to free it later */
	_set_present_idx(st, specs->pres_offset, specs->pres_size, value + 1);

	elm = &td->elements[value];
	if(elm->flags & ATF_POINTER) {
		/* Member is a pointer to another structure */
Lev Walkin's avatar
Lev Walkin committed
894
		memb_ptr2 = (void **)((char *)st + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
895 896 897 898 899 900
	} else {
		memb_ptr = (char *)st + elm->memb_offset;
		memb_ptr2 = &memb_ptr;
	}
	ASN_DEBUG("Discovered CHOICE %s encodes %s", td->name, elm->name);

901
	if(ct && ct->range_bits >= 0) {
902
		rv = elm->type->op->uper_decoder(opt_codec_ctx, elm->type,
903 904 905
			elm->per_constraints, memb_ptr2, pd);
	} else {
		rv = uper_open_type_get(opt_codec_ctx, elm->type,
Lev Walkin's avatar
Lev Walkin committed
906
			elm->per_constraints, memb_ptr2, pd);
907 908
	}

Lev Walkin's avatar
Lev Walkin committed
909
	if(rv.code != RC_OK)
910 911
		ASN_DEBUG("Failed to decode %s in %s (CHOICE) %d",
			elm->name, td->name, rv.code);
Lev Walkin's avatar
Lev Walkin committed
912 913
	return rv;
}
Lev Walkin's avatar
Lev Walkin committed
914

Lev Walkin's avatar
Lev Walkin committed
915 916
asn_enc_rval_t
CHOICE_encode_uper(asn_TYPE_descriptor_t *td,
Lev Walkin's avatar
Lev Walkin committed
917 918
                   const asn_per_constraints_t *constraints, void *sptr,
                   asn_per_outp_t *po) {
919
	asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
Lev Walkin's avatar
Lev Walkin committed
920
	asn_TYPE_member_t *elm;	/* CHOICE's element */
Lev Walkin's avatar
Lev Walkin committed
921
	const asn_per_constraint_t *ct;
Lev Walkin's avatar
Lev Walkin committed
922
	void *memb_ptr;
Lev Walkin's avatar
Lev Walkin committed
923
	unsigned present;
924
	int present_enc;
Lev Walkin's avatar
Lev Walkin committed
925

926
	if(!sptr) ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
927 928 929 930 931 932 933

	ASN_DEBUG("Encoding %s as CHOICE", td->name);

	if(constraints) ct = &constraints->value;
	else if(td->per_constraints) ct = &td->per_constraints->value;
	else ct = 0;

934
	present = _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
Lev Walkin's avatar
Lev Walkin committed
935

936
	/*
Lev Walkin's avatar
Lev Walkin committed
937 938 939
	 * If the structure was not initialized properly, it cannot be encoded:
	 * can't deduce what to encode in the choice type.
	 */
Lev Walkin's avatar
Lev Walkin committed
940
	if(present == 0 || present > td->elements_count)
941
		ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
942 943 944
	else
		present--;

945 946
	ASN_DEBUG("Encoding %s CHOICE element %d", td->name, present);

Lev Walkin's avatar
Lev Walkin committed
947 948
	/* Adjust if canonical order is different from natural order */
	if(specs->canonical_order)
949 950 951
		present_enc = specs->canonical_order[present];
	else
		present_enc = present;
Lev Walkin's avatar
Lev Walkin committed
952 953

	if(ct && ct->range_bits >= 0) {
954 955
		if(present_enc < ct->lower_bound
		|| present_enc > ct->upper_bound) {
Lev Walkin's avatar
Lev Walkin committed
956 957
			if(ct->flags & APC_EXTENSIBLE) {
				if(per_put_few_bits(po, 1, 1))
958
					ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
959
			} else {
960
				ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
961 962 963 964 965 966
			}
			ct = 0;
		}
	}
	if(ct && ct->flags & APC_EXTENSIBLE)
		if(per_put_few_bits(po, 0, 1))
967
			ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
968 969 970 971

	elm = &td->elements[present];
	if(elm->flags & ATF_POINTER) {
		/* Member is a pointer to another structure */
Lev Walkin's avatar
Lev Walkin committed
972
		memb_ptr = *(void **)((char *)sptr + elm->memb_offset);
973
		if(!memb_ptr) ASN__ENCODE_FAILED;
Lev Walkin's avatar
Lev Walkin committed
974 975 976 977
	} else {
		memb_ptr = (char *)sptr + elm->memb_offset;
	}

978
	if(ct && ct->range_bits >= 0) {
979
		if(per_put_few_bits(po, present_enc, ct->range_bits))
980
			ASN__ENCODE_FAILED;
981

982
		return elm->type->op->uper_encoder(elm->type, elm->per_constraints,
Lev Walkin's avatar
Lev Walkin committed
983
			memb_ptr, po);
984 985 986
	} else {
		asn_enc_rval_t rval;
		if(specs->ext_start == -1)
987
			ASN__ENCODE_FAILED;
988
		if(uper_put_nsnnwn(po, present_enc - specs->ext_start))
989
			ASN__ENCODE_FAILED;
990 991
		if(uper_open_type_put(elm->type, elm->per_constraints,
			memb_ptr, po))
992
			ASN__ENCODE_FAILED;
993
		rval.encoded = 0;
994
		ASN__ENCODED_OK(rval);
995
	}
Lev Walkin's avatar
Lev Walkin committed
996 997
}
   
Lev Walkin's avatar
Lev Walkin committed
998

Lev Walkin's avatar
Lev Walkin committed
999
int
Lev Walkin's avatar
Lev Walkin committed
1000
CHOICE_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
Lev Walkin's avatar
Lev Walkin committed
1001
		asn_app_consume_bytes_f *cb, void *app_key) {
Lev Walkin's avatar
Lev Walkin committed
1002
	asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
Lev Walkin's avatar
Lev Walkin committed
1003
	unsigned present;
Lev Walkin's avatar
Lev Walkin committed
1004

1005
	if(!sptr) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
Lev Walkin's avatar
Lev Walkin committed
1006 1007 1008 1009 1010 1011 1012

	/*
	 * Figure out which CHOICE element is encoded.
	 */
	present = _fetch_present_idx(sptr, specs->pres_offset,specs->pres_size);

	/*
Lev Walkin's avatar
Lev Walkin committed
1013
	 * Print that element.
Lev Walkin's avatar
Lev Walkin committed
1014
	 */
Lev Walkin's avatar
Lev Walkin committed
1015
	if(present > 0 && present <= td->elements_count) {
Lev Walkin's avatar
Lev Walkin committed
1016
		asn_TYPE_member_t *elm = &td->elements[present-1];
Lev Walkin's avatar
Lev Walkin committed
1017 1018
		const void *memb_ptr;

1019
		if(elm->flags & ATF_POINTER) {
Lev Walkin's avatar
Lev Walkin committed
1020
			memb_ptr = *(const void * const *)((const char *)sptr + elm->memb_offset);
1021
			if(!memb_ptr) return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
Lev Walkin's avatar
Lev Walkin committed
1022
		} else {
Lev Walkin's avatar
Lev Walkin committed
1023
			memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
1024 1025 1026
		}

		/* Print member's name and stuff */
Lev Walkin's avatar
Lev Walkin committed
1027
		if(0) {
1028 1029
			if(cb(elm->name, strlen(elm->name), app_key) < 0
			|| cb(": ", 2, app_key) < 0)
Lev Walkin's avatar
Lev Walkin committed
1030 1031
				return -1;
		}
Lev Walkin's avatar
Lev Walkin committed
1032

1033
		return elm->type->op->print_struct(elm->type, memb_ptr, ilevel,
Lev Walkin's avatar
Lev Walkin committed
1034 1035
			cb, app_key);
	} else {
1036
		return (cb("<absent>", 8, app_key) < 0) ? -1 : 0;
Lev Walkin's avatar
Lev Walkin committed
1037 1038 1039 1040
	}
}

void
1041 1042
CHOICE_free(const asn_TYPE_descriptor_t *td, void *ptr,
            enum asn_struct_free_method method) {
1043
    asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
Lev Walkin's avatar
Lev Walkin committed
1044
	unsigned present;
Lev Walkin's avatar
Lev Walkin committed
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058

	if(!td || !ptr)
		return;

	ASN_DEBUG("Freeing %s as CHOICE", td->name);

	/*
	 * Figure out which CHOICE element is encoded.
	 */
	present = _fetch_present_idx(ptr, specs->pres_offset, specs->pres_size);

	/*
	 * Free that element.
	 */
Lev Walkin's avatar
Lev Walkin committed
1059
	if(present > 0 && present <= td->elements_count) {
Lev Walkin's avatar
Lev Walkin committed
1060
		asn_TYPE_member_t *elm = &td->elements[present-1];
Lev Walkin's avatar
Lev Walkin committed
1061 1062
		void *memb_ptr;

1063
		if(elm->flags & ATF_POINTER) {
Lev Walkin's avatar
Lev Walkin committed
1064
			memb_ptr = *(void **)((char *)ptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
1065
			if(memb_ptr)
Lev Walkin's avatar
Lev Walkin committed
1066
				ASN_STRUCT_FREE(*elm->type, memb_ptr);
Lev Walkin's avatar
Lev Walkin committed
1067
		} else {
Lev Walkin's avatar
Lev Walkin committed
1068
			memb_ptr = (void *)((char *)ptr + elm->memb_offset);
Lev Walkin's avatar
Lev Walkin committed
1069
			ASN_STRUCT_FREE_CONTENTS_ONLY(*elm->type, memb_ptr);
Lev Walkin's avatar
Lev Walkin committed
1070 1071 1072
		}
	}

1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
    switch(method) {
    case ASFM_FREE_EVERYTHING:
        FREEMEM(ptr);
        break;
    case ASFM_FREE_UNDERLYING:
        break;
    case ASFM_FREE_UNDERLYING_AND_RESET:
        memset(ptr, 0, specs->struct_size);
        break;
    }
Lev Walkin's avatar
Lev Walkin committed
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
}


/*
 * The following functions functions offer protection against -fshort-enums,
 * compatible with little- and big-endian machines.
 * If assertion is triggered, either disable -fshort-enums, or add an entry
 * here with the ->pres_size of your target stracture.
 * Unless the target structure is packed, the ".present" member
 * is guaranteed to be aligned properly. ASN.1 compiler itself does not
 * produce packed code.
 */
1095 1096 1097 1098
static unsigned
_fetch_present_idx(const void *struct_ptr, unsigned pres_offset,
                   unsigned pres_size) {
    const void *present_ptr;
Lev Walkin's avatar
Lev Walkin committed
1099
	unsigned present;
Lev Walkin's avatar
Lev Walkin committed
1100

Lev Walkin's avatar
Lev Walkin committed
1101
	present_ptr = ((const char *)struct_ptr) + pres_offset;
Lev Walkin's avatar
Lev Walkin committed
1102 1103

	switch(pres_size) {
1104 1105 1106
	case sizeof(int):	present = *(const unsigned int *)present_ptr; break;
	case sizeof(short):	present = *(const unsigned short *)present_ptr; break;
	case sizeof(char):	present = *(const unsigned char *)present_ptr; break;
Lev Walkin's avatar
Lev Walkin committed
1107 1108 1109 1110 1111 1112 1113 1114 1115
	default:
		/* ANSI C mandates enum to be equivalent to integer */
		assert(pres_size != sizeof(int));
		return 0;	/* If not aborted, pass back safe value */
	}

	return present;
}

Lev Walkin's avatar
Lev Walkin committed
1116
static void
1117 1118 1119
_set_present_idx(void *struct_ptr, unsigned pres_offset, unsigned pres_size,
                 unsigned present) {
    void *present_ptr;
Lev Walkin's avatar
Lev Walkin committed
1120
	present_ptr = ((char *)struct_ptr) + pres_offset;
Lev Walkin's avatar
Lev Walkin committed
1121 1122

	switch(pres_size) {
1123 1124 1125
	case sizeof(int):	*(unsigned int *)present_ptr   = present; break;
	case sizeof(short):	*(unsigned short *)present_ptr = present; break;
	case sizeof(char):	*(unsigned char *)present_ptr  = present; break;
Lev Walkin's avatar
Lev Walkin committed
1126 1127 1128 1129 1130
	default:
		/* ANSI C mandates enum to be equivalent to integer */
		assert(pres_size != sizeof(int));
	}
}
1131 1132 1133

static const void *
_get_member_ptr(const asn_TYPE_descriptor_t *td, const void *sptr,
Lev Walkin's avatar
Lev Walkin committed
1134
                asn_TYPE_member_t **elm_ptr, unsigned *present_out) {
1135
    asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
Lev Walkin's avatar
Lev Walkin committed
1136
    unsigned present;
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153

    if(!sptr) {
        *elm_ptr = NULL;
        *present_out = 0;
        return NULL;
    }

    /*
	 * Figure out which CHOICE element is encoded.
	 */
	present = _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
    *present_out = present;

    /*
     * The presence index is intentionally 1-based to avoid
     * treating zeroed structure as a valid one.
     */
Lev Walkin's avatar
Lev Walkin committed
1154
	if(present > 0 && present <= td->elements_count) {
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
        asn_TYPE_member_t *const elm = &td->elements[present - 1];
        const void *memb_ptr;

		if(elm->flags & ATF_POINTER) {
            memb_ptr =
                *(const void *const *)((const char *)sptr + elm->memb_offset);
        } else {
            memb_ptr = (const void *)((const char *)sptr + elm->memb_offset);
        }
        *elm_ptr = elm;
        return memb_ptr;
    } else {
        *elm_ptr = NULL;
        return NULL;
    }

}

int
CHOICE_compare(const asn_TYPE_descriptor_t *td, const void *aptr, const void *bptr) {
    asn_TYPE_member_t *aelm;
    asn_TYPE_member_t *belm;
Lev Walkin's avatar
Lev Walkin committed
1177 1178
    unsigned apresent = 0;
    unsigned bpresent = 0;
1179 1180 1181 1182 1183 1184
    const void *amember = _get_member_ptr(td, aptr, &aelm, &apresent);
    const void *bmember = _get_member_ptr(td, bptr, &belm, &apresent);

    if(amember && bmember) {
        if(apresent == bpresent) {
            assert(aelm == belm);
1185
            return aelm->type->op->compare_struct(aelm->type, amember, bmember);
1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
        } else if(apresent < bpresent) {
            return -1;
        } else {
            return 1;
        }
    } else if(!amember) {
        return -1;
    } else {
        return 1;
    }
}
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231

/*
 * Return the 1-based choice variant presence index.
 * Returns 0 in case of error.
 */
unsigned
CHOICE_variant_get_presence(const asn_TYPE_descriptor_t *td, const void *sptr) {
    asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
    return _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
}

/*
 * Sets or resets the 1-based choice variant presence index.
 * In case a previous index is not zero, the currently selected structure
 * member is freed and zeroed-out first.
 * Returns 0 on success and -1 on error.
 */
int
CHOICE_variant_set_presence(const asn_TYPE_descriptor_t *td, void *sptr,
                            unsigned present) {
    asn_CHOICE_specifics_t *specs = (asn_CHOICE_specifics_t *)td->specifics;
    unsigned old_present;

    if(!sptr) {
        return -1;
    }

    if(present > td->elements_count)
        return -1;

    old_present =
        _fetch_present_idx(sptr, specs->pres_offset, specs->pres_size);
    if(present == old_present)
        return 0;

Lev Walkin's avatar
Lev Walkin committed
1232
    if(old_present != 0) {
1233
        assert(old_present <= td->elements_count);
1234
        ASN_STRUCT_RESET(*td, sptr);
1235 1236 1237 1238 1239 1240 1241
    }

    _set_present_idx(sptr, specs->pres_offset, specs->pres_size, present);

    return 0;
}

1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253
asn_TYPE_operation_t asn_OP_CHOICE = {
	CHOICE_free,
	CHOICE_print,
	CHOICE_compare,
	CHOICE_decode_ber,
	CHOICE_encode_der,
	CHOICE_decode_xer,
	CHOICE_encode_xer,
#ifdef	ASN_DISABLE_OER_SUPPORT
	0,
	0,
#else
Lev Walkin's avatar
Lev Walkin committed
1254 1255
	CHOICE_decode_oer,
	CHOICE_encode_oer,
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
#endif  /* ASN_DISABLE_OER_SUPPORT */
#ifdef ASN_DISABLE_PER_SUPPORT
	0,
	0,
#else
	CHOICE_decode_uper,
	CHOICE_encode_uper,
#endif	/* ASN_DISABLE_PER_SUPPORT */
	CHOICE_outmost_tag
};