PageRenderTime 75ms CodeModel.GetById 14ms app.highlight 51ms RepoModel.GetById 2ms app.codeStats 0ms

/protocols/asn/asn-impl/src/main/java/org/mobicents/protocols/asn/AsnInputStream.java

http://mobicents.googlecode.com/
Java | 1046 lines | 632 code | 189 blank | 225 comment | 194 complexity | 6668d8abcdf33164a2f3e8fbe157cb55 MD5 | raw file
   1/*
   2 * JBoss, Home of Professional Open Source
   3 * Copyright 2011, Red Hat, Inc. and individual contributors
   4 * by the @authors tag. See the copyright.txt in the distribution for a
   5 * full listing of individual contributors.
   6 *
   7 * This is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU Lesser General Public License as
   9 * published by the Free Software Foundation; either version 2.1 of
  10 * the License, or (at your option) any later version.
  11 *
  12 * This software is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15 * Lesser General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU Lesser General Public
  18 * License along with this software; if not, write to the Free
  19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21 */
  22
  23package org.mobicents.protocols.asn;
  24
  25import java.io.ByteArrayOutputStream;
  26import java.io.EOFException;
  27import java.io.IOException;
  28import java.io.InputStream;
  29import java.io.OutputStream;
  30import java.nio.ByteBuffer;
  31import java.util.BitSet;
  32
  33/**
  34 * 
  35 * @author amit bhayani
  36 * @author baranowb
  37 * @author sergey vetyutnev
  38 */
  39public class AsnInputStream extends InputStream {
  40
  41	private static final String _REAL_BASE10_CHARSET = "US-ASCII";
  42	private static final int DATA_BUCKET_SIZE = 1024;
  43
  44	private byte[] buffer;
  45	
  46	private int start;
  47	private int length;
  48	private int pos;
  49	
  50	private int tagClass = 0;
  51	private int pCBit = 0;
  52	private int tag;
  53	
  54	
  55	public AsnInputStream( byte[] buf ) {
  56		this.buffer = buf;
  57		this.length = buf.length;
  58	}
  59	
  60	public AsnInputStream( byte[] buf, int tagClass, boolean isPrimitive, int tag ) {
  61		this.buffer = buf;
  62		this.length = buf.length;
  63		
  64		this.tagClass = tagClass;
  65		if (isPrimitive)
  66			this.pCBit = 0;
  67		else
  68			this.pCBit = 1;
  69		this.tag = tag;
  70	}
  71	
  72	protected AsnInputStream( AsnInputStream buf, int start, int length ) throws IOException {
  73		this.buffer = buf.buffer;
  74		this.start = buf.start + start;
  75		this.length = length;
  76
  77		if (start < 0 || start > buf.length || this.start < 0 || this.start > this.buffer.length || this.length < 0
  78				|| this.start + this.length > this.buffer.length)
  79			throw new IOException("Bad start or length values when creating AsnInputStream");
  80		
  81		this.tagClass = buf.tagClass;
  82		this.pCBit = buf.pCBit;
  83		this.tag = buf.tag;
  84	}
  85	
  86	@Deprecated
  87	public AsnInputStream(InputStream in) {
  88		try {
  89			int av = in.available();
  90			byte[] buf = new byte[av];
  91			in.read(buf);
  92			
  93			this.buffer = buf;
  94			this.length = buf.length;
  95			
  96		} catch (IOException e) {
  97			e.printStackTrace();
  98			
  99			this.buffer = new byte[0];
 100		}
 101	}
 102	
 103	
 104	/**
 105	 * Return the current position in the stream
 106	 * 
 107	 * @return
 108	 */
 109	public int position() {
 110		return this.pos;
 111	}
 112	
 113	/**
 114	 * Set the new current position of the stream
 115	 * If the new position is bad - throws IOException
 116	 * 
 117	 * @param newPosition
 118	 * @throws IOException
 119	 */
 120	public void position( int newPosition ) throws IOException {
 121		if (newPosition < 0 || newPosition > this.length)
 122			throw new IOException("Bad newPosition value when setting the new position in the AsnInputStream");
 123		
 124		this.pos = newPosition;
 125	}
 126
 127	/**
 128	 * Return the count of available bytes to read
 129	 * 
 130	 * @return
 131	 */
 132	@Override
 133	public int available() {
 134		return this.length - this.pos;
 135	}
 136
 137	/**
 138	 * Advance the stream current position for byteCount bytes
 139	 * If the new position is bad - throws IOException
 140	 * 
 141	 * @param byteCount
 142	 * @throws IOException
 143	 */
 144	public void advance(int byteCount) throws IOException {
 145		this.position(this.pos + byteCount);
 146	}
 147
 148	@Override
 149	public long skip(long n) throws IOException {
 150		if (n < 0)
 151			n = 0;
 152		int newPosition = this.pos + (int) n;
 153		if (newPosition < 0 || newPosition > this.length)
 154			newPosition = this.length;
 155
 156		long skipCnt = newPosition - this.pos;
 157		this.pos = newPosition;
 158
 159		return skipCnt;
 160	}
 161	
 162	@Override
 163	public boolean markSupported() {
 164		return false;
 165	}
 166	
 167	/**
 168	 * Get a byte from stream and return it.
 169	 * If end of stream - throws IOException
 170	 * 
 171	 * @return
 172	 */
 173	@Override
 174	public int read() throws IOException {
 175		if (this.available() == 0)
 176			throw new EOFException("AsnInputStream has reached the end");
 177		
 178		return this.buffer[this.start + this.pos++];
 179	}
 180
 181	/**
 182	 * Fill the byte with bytes from the stream. If stream contains not enough
 183	 * data - only the part of array is filled
 184	 * 
 185	 * @param b
 186	 *            The byte array to be filled by data
 187	 * @param off
 188	 *            Offset of byte array from which fill the array
 189	 * @param len
 190	 *            Bytes count to fill
 191	 * @return Bytes count that have really read
 192	 * @throws IOException
 193	 */
 194	@Override
 195	public int read(byte[] b, int off, int len) throws IOException {
 196		
 197		if (len > b.length)
 198			len = b.length;
 199		
 200		int cnt = this.available();
 201		if (cnt > len)
 202			cnt = len;
 203		
 204		if (b == null || off < 0 || len < 0 || off + len > b.length)
 205			throw new EOFException("Target byte array is null or bad off or len values");
 206
 207		System.arraycopy(this.buffer, this.start + this.pos, b, off, cnt);
 208		this.pos += cnt;
 209		
 210		return cnt;
 211	}
 212	
 213	/**
 214	 * Fill the byte with bytes from the stream. If stream contains not enough
 215	 * data - only the part of array is filled
 216	 * 
 217	 * @param b
 218	 *            The byte array to be filled by data
 219	 * @return Bytes count that have really read
 220	 * @throws IOException
 221	 */
 222	@Override
 223	public int read(byte[] b) throws IOException {
 224
 225		if (b == null )
 226			throw new EOFException("Target byte array is null");
 227
 228		return this.read(b, 0, b.length);
 229	}
 230
 231	/**
 232	 * Reads the tag field. Returns the tag value.
 233	 * Tag class and primitive / constructive mark can be get then by getTagClass() and isTagPrimitive() methods
 234	 * 
 235	 * @return
 236	 * @throws IOException
 237	 */
 238	public int readTag() throws IOException {
 239		byte b = (byte) this.read();
 240
 241		this.tagClass = (b & Tag.CLASS_MASK) >> 6;
 242		this.pCBit = (b & Tag.PC_MASK) >> 5;
 243
 244		this.tag = b & Tag.TAG_MASK;
 245
 246		// For larger tag values, the first octet has all ones in bits 5 to 1,
 247		// and the tag value is then encoded in
 248		// as many following octets as are needed, using only the least
 249		// significant seven bits of each octet,
 250		// and using the minimum number of octets for the encoding. The most
 251		// significant bit (the "more"
 252		// bit) is set to 1 in the first following octet, and to zero in the
 253		// last.
 254		if (tag == Tag.TAG_MASK) {
 255			byte temp;
 256			tag = 0;
 257			do {
 258				temp = (byte) this.read();
 259				tag = (tag << 7) | (0x7F & temp);
 260			} while (0 != (0x80 & temp));
 261		}
 262
 263		return tag;
 264	}
 265
 266	public int getTagClass() {
 267		return tagClass;
 268	}
 269
 270	public int getTag() {
 271		return tag;
 272	}
 273
 274	public boolean isTagPrimitive() {
 275		return pCBit == Tag.PC_PRIMITIVITE;
 276	}
 277
 278	/**
 279	 * Reads and returns the length field.
 280	 * In case of indefinite length returns Tag.Indefinite_Length value
 281	 * 
 282	 * @return
 283	 * @throws IOException
 284	 */
 285	public int readLength() throws IOException {
 286		int length = 0;
 287
 288		byte b = (byte) this.read();
 289
 290		// This is short form. The short form can be used if the number of
 291		// octets in the Value part is less than or
 292		// equal to 127, and can be used whether the Value part is primitive or
 293		// constructed. This form is identified by
 294		// encoding bit 8 as zero, with the length count in bits 7 to 1 (as
 295		// usual, with bit 7 the most significant bit
 296		// of the length).
 297		if ((b & 0x80) == 0) {
 298			return b;
 299		}
 300
 301		// This is indefinite form. The indefinite form of length can only be
 302		// used (but does not have to be) if the V
 303		// part is constructed, that
 304		// is to say, consists of a series of TLVs. In the indefinite form of
 305		// length the first bit of the first octet is
 306		// set to 1, as for the long form, but the value N is set to zero.
 307		b = (byte) (b & 0x7F);
 308		if (b == 0) {
 309			return Tag.Indefinite_Length;
 310		}
 311
 312		// If bit 8 of the first length octet is set to 1, then we have the long
 313		// form of length. In long form, the first
 314		// octet encodes in its remaining seven bits a value N which is the
 315		// length of a series of octets that themselves
 316		// encode the length of the Value part.
 317		byte temp;
 318		for (int i = 0; i < b; i++) {
 319			temp = (byte) this.read();
 320			length = (length << 8) | (0x00FF & temp);
 321		}
 322
 323		return length;
 324	}
 325	
 326	/**
 327	 * This method can be invoked after the sequence tag has been read
 328	 * Returns the AsnInputStream that contains the sequence data
 329	 * The origin stream advances to the begin of the next record  
 330	 * 
 331	 * @return
 332	 * @throws AsnException
 333	 * @throws IOException
 334	 */
 335	public AsnInputStream readSequenceStream() throws AsnException, IOException {
 336
 337		int length = readLength();
 338		return this.readSequenceStreamData(length);
 339	}
 340	
 341	/**
 342	 * This method can be invoked after the sequence tag has been read
 343	 * Returns the byte array that contains the sequence data
 344	 * The origin stream advances to the begin of the next record
 345	 *   
 346	 * @return
 347	 * @throws AsnException
 348	 * @throws IOException
 349	 */
 350	public byte[] readSequence() throws AsnException, IOException {
 351
 352		int length = readLength();
 353		return this.readSequenceData(length);
 354	}
 355
 356	/**
 357	 * This method can be invoked after the sequence tag and length has been
 358	 * read. Returns the AsnInputStream that contains the sequence data. The origin
 359	 * stream advances to the begin of the next record
 360	 * 
 361	 * @param length
 362	 *            The sequence length
 363	 * @return
 364	 * @throws AsnException
 365	 * @throws IOException
 366	 */
 367	public AsnInputStream readSequenceStreamData(int length) throws AsnException, IOException {
 368
 369		if (length == Tag.Indefinite_Length) {
 370			return this.readSequenceIndefinite();
 371		} else {
 372			int startPos = this.pos;
 373			this.advance(length);
 374			return new AsnInputStream(this, startPos, length);
 375		}
 376	}
 377	
 378	/**
 379	 * This method can be invoked after the sequence tag and length has been
 380	 * read. Returns the byte stream that contains the sequence data. The origin
 381	 * stream advances to the begin of the next record
 382	 * 
 383	 * @param length
 384	 *            The sequence length
 385	 * @return
 386	 * @throws AsnException
 387	 * @throws IOException
 388	 */
 389	public byte[] readSequenceData(int length) throws AsnException, IOException {
 390
 391		AsnInputStream ais = this.readSequenceStreamData(length);
 392		byte[] res = new byte[ais.length];
 393		System.arraycopy(ais.buffer, ais.start + ais.pos, res, 0, ais.length);
 394		return res;
 395	}
 396
 397	public AsnInputStream readSequenceIndefinite() throws AsnException, IOException {
 398		
 399		int startPos = this.pos;
 400		this.advanceIndefiniteLength();
 401		return new AsnInputStream(this, startPos, this.pos - startPos - 2);
 402	}
 403
 404	public byte[] readIndefinite() throws AsnException, IOException {
 405		
 406		int startPos = this.pos;
 407		this.advanceIndefiniteLength();
 408		
 409		byte[] res = new byte[this.pos - startPos - 2];
 410		System.arraycopy(this.buffer, this.start + startPos, res, 0, this.pos - startPos - 2);
 411		return res;
 412	
 413	}
 414	
 415	private void advanceIndefiniteLength() throws AsnException, IOException {
 416		
 417		while (this.available() > 0) {
 418
 419			// found End-of-contents tag
 420			int tag = this.readTag();
 421			if (tag == 0 && this.tagClass == 0) {
 422				if (this.read() == 0)
 423					return;
 424				else
 425					throw new AsnException("End-of-contents tag must have the zero length");
 426			}			
 427			
 428			int length = this.readLength();
 429			if (length == Tag.Indefinite_Length)
 430				this.advanceIndefiniteLength();
 431			else
 432				this.advance(length);
 433		}
 434	}
 435	
 436	/**
 437	 * Skip length and content fields of primitive and constructed element (definite and indefinite length supported)
 438	 * 
 439	 * @throws IOException
 440	 * @throws AsnException
 441	 */
 442	public void advanceElement() throws IOException, AsnException {
 443		int length = this.readLength();
 444		this.advanceElementData(length);
 445	}
 446	
 447	/**
 448	 * Skip content field of primitive and constructed element (definite and indefinite length supported)
 449	 * 
 450	 * @param length
 451	 * @throws IOException
 452	 * @throws AsnException
 453	 */
 454	public void advanceElementData(int length) throws IOException, AsnException {
 455		if( length==Tag.Indefinite_Length )
 456			this.advanceIndefiniteLength();
 457		else
 458			this.advance(length);
 459	}
 460	
 461	public boolean readBoolean() throws AsnException, IOException {
 462		
 463		int length = readLength();
 464		return this.readBooleanData(length);
 465	}
 466	
 467	public boolean readBooleanData(int length) throws AsnException, IOException {
 468
 469		if (this.pCBit != 0 || length != 1)
 470			throw new AsnException("Failed when parsing the Boolean field: this field must be primitive and the length must be equal 1");
 471		
 472		byte temp = (byte) this.read();
 473
 474		// If temp is not zero stands for true irrespective of actual Value
 475		return (temp != 0);
 476	}
 477	
 478	public long readInteger() throws AsnException, IOException {
 479		
 480		int length = this.readLength();
 481		return this.readIntegerData(length);
 482	}
 483	
 484	public long readIntegerData(int length) throws AsnException, IOException {
 485		long value = 0;
 486		byte temp;
 487
 488		if (this.pCBit != 0 || length == 0 || length == Tag.Indefinite_Length)
 489			throw new AsnException("Failed when parsing the Interger field: this field must be primitive and have the length more then zero");
 490
 491		temp = (byte) this.read();
 492		value = temp;
 493
 494		for (int i = 0; i < length - 1; i++) {
 495			temp = (byte) this.read();
 496			value = (value << 8) | (0x00FF & temp);
 497		}
 498
 499		return value;
 500	}
 501	
 502	public double readReal() throws AsnException, IOException {
 503
 504		int length = readLength();
 505		return readRealData(length);
 506	}
 507	
 508	public double readRealData(int length) throws AsnException, IOException {
 509
 510		if (this.pCBit != 0 || length == Tag.Indefinite_Length)
 511			throw new AsnException("Failed when parsing the Real field: this field must be primitive");
 512		
 513		// universal part
 514		if (length == 0) {
 515			// yeah, nice
 516			return 0.0;
 517		}
 518
 519		if (length == 1) {
 520			// +INF/-INF
 521			int b = this.read() & 0xFF;
 522			if (b == 0x40) {
 523				return Double.POSITIVE_INFINITY;
 524			} else if (b == 0x41) {
 525				return Double.NEGATIVE_INFINITY;
 526			} else {
 527				throw new AsnException(
 528						"Failed when parsing the Real field: Real length indicates positive/negative infinity, but value is wrong: "
 529								+ Integer.toBinaryString(b));
 530			}
 531		}
 532		int infoBits = this.read();
 533		// substract on for info bits
 534		length--;
 535
 536		// only binary has first bit of info set to 1;
 537		// now the tricky part, this takes into account base10
 538		if ((infoBits & 0xC0) == 0) {
 539			
 540			// FIXME: add check on boundry of simple length
 541			// encoded as char string
 542			// IA5 == ASCII...?
 543			// .............................
 544			String nrRep = new String(this.buffer, this.start + this.pos, length, _REAL_BASE10_CHARSET);
 545			// this will swallow NR(1-3) and give proper double :)
 546			return Double.parseDouble(nrRep);
 547			// .............................
 548			
 549		} else if((infoBits & 0x80) == 0x80) {
 550
 551			// encoded binary - mantisa and all that funny digits.
 552			// the REAL type has been semantically equivalent to the
 553			// type:
 554			// [UNIVERSAL 9] IMPLICIT SEQUENCE {
 555			// mantissa INTEGER (ALL EXCEPT 0),
 556			// base INTEGER (2|10),
 557			// exponent INTEGER }
 558			// sign x N x (2 ^ scale) x (base ^ E); --> base ^ E == 2 ^(E+x) ==
 559			// where x
 560			int tmp = 0;
 561
 562			int signBit = (infoBits & BERStatics.REAL_BB_SIGN_MASK) << 1;
 563			// now lets determine length of e(exponent) and n(positive integer)
 564			long e = 0;
 565			int s = (infoBits & BERStatics.REAL_BB_SCALE_MASK) >> 2;
 566
 567			tmp = infoBits & BERStatics.REAL_BB_EE_MASK;
 568			if (tmp == 0x0) {
 569				e = this.read() & 0xFF;
 570				length--;
 571				// real representation
 572			} else if (tmp == 0x01) {
 573				e = (this.read() & 0xFF) << 8;
 574				length--;
 575				e |= this.read() & 0xFF;
 576				length--;
 577				if (e > 0x7FF) {
 578
 579					// to many bits... Double
 580					throw new AsnException(
 581							"Exponent part has to many bits lit, allowed are 11, present: "
 582									+ Long.toBinaryString(e));
 583				}
 584				// prepare E to become bits - this may cause loose of data,
 585				e &= 0x7FF;
 586			} else {
 587				// this is too big for java to handle.... we can have up to 11
 588				// bits..
 589				throw new AsnException(
 590						"Exponent part has to many bits lit, allowed are 11, but stream indicates 3 or more octets");
 591			}
 592			// now we may read up to 52bits
 593			// 7*8 == 56, we need up to 52
 594			if (length > 7) {
 595				throw new AsnException(
 596						"Length exceeds JAVA double mantisa size");
 597			}
 598
 599			long n = 0;
 600			while (length > 0) {
 601				--length;
 602				long readV = (((long) this.read() << 32) >>> 32) & 0xFF;
 603
 604				readV = readV << (length * 8);
 605
 606				n |= readV;
 607			}
 608
 609			// check for possible overflow
 610			if ((n & 0x0FFFFFFF) > 4503599627370495L) { // num is 11 bits lit to
 611				// "1"
 612				throw new AsnException("Overflow on mantisa");
 613			}
 614			// we have real part, now lets add that scale; this is M x (2^F),
 615			// which essentialy is bit shift :)
 616			int shift = (int) Math.pow(2, s) - 1; // -1 for 2, where we dont
 617			// shift
 618			n = n << (shift); // this might be bad code.
 619
 620			// now lets take care of different base, we are base2: base8 ==
 621			// base2^3,base16== base2^4
 622			int base = (infoBits & BERStatics.REAL_BB_BASE_MASK) >> 4;
 623			// is this correct?
 624			if (base == 0x01) {
 625				e = e * 3; // (2^3)^e
 626			} else if (base == 0x10) {
 627				e = e * 4; // (2^4)^e
 628			}
 629			// do check again.
 630			if (e > 0x7FF) {
 631				// to many bits... Double
 632				throw new AsnException(
 633						"Exponent part has to many bits lit, allowed are 11, present: "
 634								+ Long.toBinaryString(e));
 635			}
 636
 637			// double is 8bytes
 638			byte[] doubleRep = new byte[8];
 639			// set sign, no need to shift
 640			doubleRep[0] = (byte) (signBit);
 641			// now get first 7 bits of e;
 642			doubleRep[0] |= ((e >> 4) & 0xFF);
 643			doubleRep[1] = (byte) ((e & 0x0F) << 4);
 644			// from back its easier
 645			doubleRep[7] = (byte) n;
 646			doubleRep[6] = (byte) (n >> 8);
 647			doubleRep[5] = (byte) (n >> 16);
 648			doubleRep[4] = (byte) (n >> 24);
 649			doubleRep[3] = (byte) (n >> 32);
 650			doubleRep[2] = (byte) (n >> 40);
 651			doubleRep[1] |= (byte) ((n >> 48) & 0x0F);
 652			ByteBuffer bb = ByteBuffer.wrap(doubleRep);
 653			return bb.getDouble();
 654		} else {
 655			throw new AsnException("Failed when parsing the Real field: Unknown infoBits: " + infoBits);
 656		}
 657	}
 658
 659	public BitSetStrictLength readBitString() throws AsnException, IOException {
 660		
 661		int length = this.readLength();
 662		return this.readBitStringData(length);
 663	}
 664	
 665	public BitSetStrictLength readBitStringData(int length) throws AsnException, IOException {
 666
 667		BitSetStrictLength bitSet = new BitSetStrictLength(0);
 668		int bitCount = this._readBitString(bitSet, length, 0);
 669		bitSet.setStrictLength(bitCount);
 670		
 671		return bitSet;
 672	}
 673
 674	@Deprecated
 675	public void readBitString(BitSet bitSet) throws AsnException, IOException {
 676		
 677		int length = this.readLength();
 678		this.readBitStringData(bitSet, length);
 679	}
 680
 681	@Deprecated
 682	public void readBitStringData(BitSet bitSet, int length) throws AsnException, IOException {
 683		this._readBitString(bitSet, length, 0);
 684	}
 685
 686	@Deprecated
 687	public void readBitStringData(BitSet bitSet, int length, boolean isTagPrimitive) throws AsnException, IOException {
 688		if (isTagPrimitive)
 689			this.pCBit = 0;
 690		else
 691			this.pCBit = 1;
 692		
 693		this._readBitString(bitSet, length, 0);
 694	}
 695
 696	private int _readBitString(BitSet bitSet, int length, int counter) throws AsnException,
 697			IOException {
 698
 699		int bits = 0;
 700
 701		if (this.pCBit == 0) {
 702			int pad = this.read();
 703
 704			// TODO We are assuming that there is always pad, even if it is 00.
 705			// This may not be true for some
 706			// Constructed
 707			// BitString where padding is only applied to last TLV. In which
 708			// case this algo is incorrect
 709			for (int count = 1; count < (length - 1); count++) {
 710				byte dataByte = (byte) this.read();
 711				for (bits = 0; bits < 8; bits++) {
 712					if (0 != (dataByte & (0x80 >> bits))) {
 713						bitSet.set(counter);
 714					}
 715					++counter;
 716				}
 717			}
 718
 719			byte lastByte = (byte) this.read();
 720			for (bits = 0; bits < (8 - pad); bits++) {
 721				if (0 != (lastByte & (0x80 >> bits))) {
 722					bitSet.set(counter);
 723				}
 724				++counter;
 725			}
 726
 727			return counter;
 728
 729		} else {
 730			if (length == Tag.Indefinite_Length) {
 731				while (true) {
 732					int tag = this.readTag();
 733					if (tag == 0) {
 734						length = this.read();
 735						if (length == 0)
 736							break;
 737						else
 738							throw new AsnException("Error while decoding the bit-string: End-of-contents tag must have the zero length");
 739					}
 740
 741					if (tag != Tag.STRING_BIT || this.tagClass != Tag.CLASS_UNIVERSAL)
 742						throw new AsnException("Error while decoding the bit-string: subsequent bit string tag must be CLASS_UNIVERSAL - STRING_BIT");
 743
 744					int length2 = this.readLength();
 745					counter = _readBitString(bitSet, length2, counter);
 746				}
 747			} else {
 748				int startPos = this.pos; 
 749				while (true) {
 750					if (this.pos > startPos + length)
 751						throw new AsnException("Error while decoding the bit-string: constructed bit-string content do not fit its length");
 752					if (this.pos == startPos + length)
 753						break;
 754
 755					int tag = this.readTag();
 756					if (tag != Tag.STRING_BIT || this.tagClass != Tag.CLASS_UNIVERSAL)
 757						throw new AsnException("Error while decoding the bit-string: subsequent bit string string tag must be CLASS_UNIVERSAL - STRING_BIT");
 758
 759					int length2 = this.readLength();
 760					if (this.pos + length2 > startPos + length)
 761						throw new AsnException("Error while decoding the bit-string: subsequent bit string is unconsistent");
 762					
 763					counter = _readBitString(bitSet, length2, counter);
 764				}
 765			}
 766			return counter;
 767		}
 768	}
 769	
 770	public byte[] readOctetString() throws AsnException, IOException {
 771		
 772		int length = this.readLength();
 773		return this.readOctetStringData(length);
 774	}
 775		
 776	@Deprecated
 777	public void readOctetString(OutputStream outputStream) throws AsnException, IOException {
 778		
 779		int length = this.readLength();
 780		this.readOctetStringData(outputStream, length);
 781	}
 782
 783	public byte[] readOctetStringData(int length) throws AsnException, IOException {
 784
 785		if (this.pCBit == 0) {
 786			if (length == Tag.Indefinite_Length)
 787				throw new AsnException("Error while decoding the octet-string: primitive with Indefinite_Length");
 788			byte[] buf = new byte[length];
 789			int cnt = this.read(buf);
 790			if (cnt != length)
 791				throw new AsnException("Error while decoding the octet-string: not enouph data for the octet string");
 792			return buf;
 793		} else {
 794			ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 795			this._readOctetString(outputStream, length);
 796			return outputStream.toByteArray();
 797		}
 798	}
 799
 800	@Deprecated
 801	public void readOctetStringData(OutputStream outputStream, int length) throws AsnException, IOException {
 802
 803		this._readOctetString(outputStream, length);
 804	}
 805
 806	@Deprecated
 807	public void readOctetStringData(OutputStream outputStream, int length, boolean isTagPrimitive) throws AsnException, IOException {
 808		
 809		if (isTagPrimitive)
 810			this.pCBit = 0;
 811		else
 812			this.pCBit = 1;
 813
 814		this._readOctetString(outputStream, length);
 815	}
 816	
 817	private void _readOctetString(OutputStream outputStream, int length) throws AsnException, IOException {
 818
 819		if (this.pCBit == 0) {
 820			
 821			this.fillOutputStream(outputStream, length);
 822		} else {
 823			
 824			if (length == Tag.Indefinite_Length) {
 825				while (true) {
 826					int tag = this.readTag();
 827					if (tag == 0) {
 828						length = this.read();
 829						if (length == 0)
 830							break;
 831						else
 832							throw new AsnException("Error while decoding the octet-string: End-of-contents tag must have the zero length");
 833					}
 834
 835					if (tag != Tag.STRING_OCTET || this.tagClass != Tag.CLASS_UNIVERSAL)
 836						throw new AsnException("Error while decoding the octet-string: subsequent octet string tag must be CLASS_UNIVERSAL - STRING_BIT");
 837
 838					int length2 = this.readLength();
 839					this._readOctetString(outputStream, length2);
 840				}
 841			} else {
 842				int startPos = this.pos; 
 843				while (true) {
 844					if (this.pos == startPos + length)
 845						break;
 846
 847					int tag = this.readTag();
 848					if (tag != Tag.STRING_OCTET || this.tagClass != Tag.CLASS_UNIVERSAL)
 849						throw new AsnException("Error while decoding the octet-string: subsequent octet string string tag must be CLASS_UNIVERSAL - STRING_BIT");
 850
 851					int length2 = this.readLength();
 852					if (this.pos + length2 > startPos + length)
 853						throw new AsnException("Error while decoding the octet-string: subsequent octet string is unconsistent");
 854					
 855					this._readOctetString(outputStream, length2);
 856				}
 857			}
 858		}
 859	}	
 860
 861	// private helper methods -------------------------------------------------
 862	private void fillOutputStream(OutputStream stream, int length)
 863			throws AsnException, IOException {
 864		byte[] dataBucket = new byte[DATA_BUCKET_SIZE];
 865		int readCount;
 866
 867		while (length != 0) {
 868			int cnt = length < DATA_BUCKET_SIZE ? length : DATA_BUCKET_SIZE;
 869			readCount = read(dataBucket, 0, cnt);
 870			if (readCount < cnt)
 871				throw new AsnException("input stream has reached the end");
 872			stream.write(dataBucket, 0, readCount);
 873			length -= readCount;
 874		}
 875	}
 876	
 877	public void readNull() throws AsnException, IOException {
 878//		int tagValue = this.readTag();
 879
 880		int length = readLength();
 881		this.readNullData(length);
 882	}
 883
 884	public void readNullData(int length) throws AsnException, IOException {
 885		if (this.pCBit != 0 || length != 0)
 886			throw new AsnException("Failed when parsing the NULL field: this field must be primitive and the length must be equal 0");
 887	}
 888	
 889	public long[] readObjectIdentifier() throws AsnException, IOException {
 890
 891		int length = readLength();
 892		return this.readObjectIdentifierData(length);
 893	}
 894		
 895	public long[] readObjectIdentifierData(int length) throws AsnException, IOException {
 896
 897		if (this.pCBit != 0 || length == Tag.Indefinite_Length)
 898			throw new AsnException("Failed when parsing the ObjectIdentifier field: this field must be primitive and the length must be defined");
 899		
 900		byte[] data = new byte[length];
 901		read(data);
 902
 903		length = 2;
 904		for (int i = 1; i < data.length; ++i) {
 905			if (data[i] >= 0)
 906				++length;
 907		}
 908		
 909		long[] oids = new long[length];
 910		int b = 0x00FF & data[0];
 911
 912		// The first octet has value 40 * value1 + value2.
 913		oids[0] = b / 40;
 914		if (oids[0] == 0 || oids[0] == 1)
 915			oids[1] = b % 40;
 916		else {
 917			oids[0] = 2;
 918			oids[1] = b - 80;
 919		}
 920
 921		int v = 0;
 922		length = 2;
 923		for (int i = 1; i < data.length; ++i) {
 924
 925			byte b1 = data[i];
 926			if ((b1 & 0x80) != 0x0) {
 927				v = (v << 7) | ((b1 & 0x7F));
 928			} else {
 929				v = (v << 7) | (b1 & 0x7F);
 930				oids[length++] = v;
 931				v = 0;
 932			}
 933		}
 934		
 935		if (length == oids.length)
 936			return oids;
 937		else {
 938			long[] oids2 = new long[length];
 939			System.arraycopy(oids, 0, oids2, 0, length);
 940			return oids2;
 941		}
 942	}
 943
 944	public String readIA5String() throws AsnException, IOException {
 945
 946		int length = readLength();
 947		return readString(BERStatics.STRING_IA5_ENCODING, Tag.STRING_IA5, length);
 948	}
 949
 950	public String readIA5StringData(int length) throws AsnException, IOException {
 951
 952		return readString(BERStatics.STRING_IA5_ENCODING, Tag.STRING_IA5, length);
 953	}
 954
 955	public String readUTF8String() throws AsnException, IOException {
 956
 957		int length = readLength();
 958		return readString(BERStatics.STRING_UTF8_ENCODING, Tag.STRING_UTF8, length);
 959	}
 960
 961	public String readUTF8StringData(int length) throws AsnException, IOException {
 962
 963		return readString(BERStatics.STRING_UTF8_ENCODING, Tag.STRING_UTF8, length);
 964	}
 965
 966	public String readGraphicString() throws AsnException, IOException {
 967
 968		int length = readLength();
 969		return readString(BERStatics.STRING_IA5_ENCODING, Tag.STRING_GRAPHIC, length);
 970	}
 971
 972	public String readGraphicStringData(int length) throws AsnException, IOException {
 973
 974		return readString(BERStatics.STRING_IA5_ENCODING, Tag.STRING_GRAPHIC, length);
 975	}
 976
 977	private String readString(String charset, int tagValue, int length) throws IOException,
 978			AsnException {
 979		
 980		if (pCBit == 0) {
 981			byte[] buf = new byte[length];
 982			int readCnt = this.read(buf);
 983			if (readCnt < length)
 984				throw new AsnException("Error decoding string fieald: not enough data in the stream");
 985			
 986			String s = new String(buf, charset);
 987			return s;
 988		} else {
 989			ByteArrayOutputStream bos = new ByteArrayOutputStream();
 990			this.readConstructedString(bos, tagValue, length);
 991
 992			String s = new String(bos.toByteArray(), charset);
 993			return s;
 994		}
 995	}
 996
 997	private void readConstructedString(ByteArrayOutputStream bos, int parentTag, int length)
 998			throws AsnException, IOException {
 999
1000		AsnInputStream ais = this.readSequenceStreamData(length);		
1001		
1002		while(true) {
1003			if (ais.available() == 0)
1004				break;
1005			
1006			int localTag = ais.readTag();
1007			if (parentTag != localTag)
1008				throw new AsnException("Error decoding string fieald: Parent tag=" + parentTag + ", does not match member tag=" + localTag);
1009			
1010			int localLength = ais.readLength();
1011			if (ais.pCBit == 0) {
1012				byte[] buf = new byte[localLength];
1013				int readCnt = ais.read(buf);
1014				if (readCnt < localLength)
1015					throw new AsnException("Error decoding string fieald: not enough data in the stream");
1016				bos.write(buf);
1017			} else {
1018				ais.readConstructedString(bos, parentTag, localLength);
1019			}
1020		}
1021	}
1022	
1023	@Override
1024	public String toString() {
1025		StringBuilder sb = new StringBuilder();
1026		
1027		sb.append("Size=");
1028		sb.append(this.length);
1029		sb.append(", Pos=");
1030		sb.append(this.pos);
1031		sb.append(", Tag=");
1032		sb.append(this.tag);
1033		sb.append(", TagClass=");
1034		sb.append(this.tagClass);
1035		sb.append(", pCBit=");
1036		sb.append(this.pCBit);
1037		sb.append("\n");
1038
1039		byte[] bf = new byte[this.length];
1040		System.arraycopy(this.buffer, this.start, bf, 0, this.length);
1041		sb.append(AsnOutputStream.arrayToString(bf));
1042
1043		return sb.toString();
1044	}
1045}
1046