1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.asn1.ber ;
18
19
20 import java.nio.ByteBuffer;
21 import java.util.List;
22
23 import org.apache.commons.lang.ArrayUtils;
24
25
26 /***
27 * TLV Tuple used by the value chunking decoder. Because the length field is
28 * a primitive int it's maximum value is 2,147,483,647 a single TLV's tuple
29 * cannot have a length over this amount or a value size over 2 GB.
30 *
31 * @author <a href="mailto:dev@directory.apache.org">
32 * Apache Directory Project</a>
33 * @version $Rev: 157644 $
34 */
35 public class Tuple
36 {
37 /*** empty buffer reused for handling null */
38 private static final ByteBuffer EMPTY_BUFFER =
39 ByteBuffer.wrap( ArrayUtils.EMPTY_BYTE_ARRAY ) ;
40
41 /*** mask for bit 5 with 0-based index */
42 private static final int BIT_5 = 0x20 ;
43 /*** mask for bit 6 with 0-based index */
44 private static final int BIT_6 = 0x40 ;
45 /*** mask for bit 7 with 0-based index */
46 private static final int BIT_7 = 0x80 ;
47
48 /*** precalculated left shift of 1 by 14 places */
49 private static final int BIT_13 = 1 << 14 ;
50 /*** precalculated left shift of 1 by 16 places */
51 private static final int BIT_15 = 1 << 16 ;
52 /*** precalculated left shift of 1 by 21 places */
53 private static final int BIT_20 = 1 << 21 ;
54 /*** precalculated left shift of 1 by 24 places */
55 private static final int BIT_23 = 1 << 24 ;
56 /*** precalculated left shift of 1 by 28 places */
57 private static final int BIT_27 = 1 << 28 ;
58
59 /*** the raw tag data */
60 int rawTag = 0 ;
61 /*** the tag id for this TLV tuple */
62 int id = 0 ;
63 /*** the flag for whether or not this TLV is constructed or primitive */
64 boolean isPrimitive = true ;
65 /*** the type class for this TLV */
66 TypeClass typeClass = TypeClass.APPLICATION ;
67 /*** the length for this TLV tuple's value field */
68 int length = 0 ;
69 /*** the present value chunk buffer read for this TLV tuple */
70 ByteBuffer valueChunk = EMPTY_BUFFER ;
71
72 /*** tlv byte index */
73 int index = Length.UNDEFINED ;
74 /*** tlv value index for how far into the value we have read */
75 int valueIndex = Length.UNDEFINED ;
76
77
78
79
80
81
82
83 /***
84 * Empty do nothing tuple.
85 */
86 public Tuple()
87 {
88 }
89
90
91 /***
92 * Creates constructed application type tlv tuples. Constructed TLV's with
93 * a definate length will use this constructor predominantly. The TypeClass
94 * defualts to APPLICATION.
95 *
96 * @param id the tag id of the tlv
97 * @param length the length of the value which is the length of all the
98 * nested tuples.
99 */
100 public Tuple( int id, int length )
101 {
102 this( id, length, TypeClass.APPLICATION ) ;
103 }
104
105
106 /***
107 * Creates constructed application type tlv tuples. Constructed TLV's with
108 * a definate length will use this constructor predominantly.
109 *
110 * @param id the tag id of the tlv
111 * @param length the length of the value which is the length of all the
112 * nested tuples.
113 * @param typeClass the type class of this tlv tuple
114 */
115 public Tuple( int id, int length, TypeClass typeClass )
116 {
117 this.id = id ;
118 this.length = length ;
119 valueChunk = EMPTY_BUFFER ;
120 isPrimitive = false ;
121
122 if ( typeClass != null )
123 {
124 this.typeClass = typeClass ;
125 }
126 }
127
128
129 /***
130 * Creates constructed application type tlv tuples. Constructed TLV's with
131 * a definate length will use this constructor predominantly.
132 *
133 * @param id the tag id of the tlv
134 * @param length the length of the value which is the length of all the
135 * nested tuples.
136 * @param isPrimitive whether or not this Tuple is primitive or constructed
137 * @param typeClass the type class of this tlv tuple
138 */
139 public Tuple( int id, int length, boolean isPrimitive, TypeClass typeClass )
140 {
141 this.id = id ;
142 this.length = length ;
143 valueChunk = EMPTY_BUFFER ;
144 isPrimitive = false ;
145
146 if ( typeClass != null )
147 {
148 this.typeClass = typeClass ;
149 }
150 }
151
152
153 /***
154 * Creates a tuple where the length is indefinite. The tuple according to
155 * the BER encoding must be of the constructed type.
156 *
157 * @param id the tag id of the tlv
158 * @param typeClass the type class for the tlv
159 */
160 public Tuple( int id, TypeClass typeClass )
161 {
162 this.id = id ;
163 this.isPrimitive = false ;
164 valueChunk = EMPTY_BUFFER ;
165 length = Length.INDEFINITE ;
166
167 if ( typeClass != null )
168 {
169 this.typeClass = typeClass ;
170 }
171 }
172
173
174
175
176
177
178
179 /***
180 * Gets the tag id for this TLV Tuple.
181 *
182 * @return the tag id
183 */
184 public int getId()
185 {
186 return id ;
187 }
188
189
190 /***
191 * Sets the id of this Tuple and as a side effect the rawTag.
192 *
193 * @param id the new tag id to set
194 */
195 public void setId( int id )
196 {
197 this.id = id ;
198 rawTag = Tag.setIntEncodedId( rawTag, id );
199 }
200
201
202 /***
203 * Gets the raw tag as it is stuffed into a primitive int.
204 *
205 * @return a primitive int stuffed with the first four octets of the tag
206 */
207 public int getRawTag()
208 {
209 return rawTag ;
210 }
211
212
213 /***
214 * Sets the raw tag encoded as a primitive int and as a side effect this
215 * call also sets the id, primitive flag, and typeClass of this TLV tuple.
216 *
217 * @param rawTag the raw primitive int encoded tag.
218 */
219 public void setRawTag( int rawTag )
220 {
221 this.rawTag = rawTag;
222 this.id = Tag.getTagId( rawTag );
223 this.isPrimitive = ! Tag.isRawTagConstructed( rawTag );
224 this.typeClass = TypeClass.getTypeClass( rawTag >> 24 );
225 }
226
227
228 /***
229 * Sets the tag parameters using a tag enumeration type. This operation
230 * sets the id, isPrimitive, typeClass, and rawTag fields at the same time.
231 *
232 * @param tag the tag enumeration constant
233 */
234 public void setTag( TagEnum tag )
235 {
236 this.rawTag = tag.getValue();
237 this.id = tag.getTagId();
238 this.isPrimitive = ! Tag.isRawTagConstructed( tag.getValue() ) ;
239 this.typeClass = tag.getTypeClass();
240 }
241
242
243 /***
244 * Sets the tag parameters using a tag enumeration type explicitly setting
245 * the primitive/constructed bit. This operation sets the id, isPrimitive,
246 * typeClass, and rawTag fields at the same time.
247 *
248 * @param tag the tag enumeration constant
249 * @param isPrimitive primitive/constructed bit override
250 */
251 public void setTag( TagEnum tag, boolean isPrimitive )
252 {
253 this.rawTag = tag.getValue();
254 this.id = tag.getTagId();
255 this.isPrimitive = isPrimitive;
256 this.typeClass = tag.getTypeClass();
257 }
258
259
260 /***
261 * Gets the raw tag with the primitive/constructed flag dubbed out.
262 * Effectively this makes every tag appear primitive and is done
263 * to remove encoding ambiguities that could interfere with pattern
264 * matching.
265 *
266 * @return the raw tag with the primitive/constructed flag dubbed out
267 */
268 public int getRawPrimitiveTag()
269 {
270 return rawTag & 0xDFFFFFFF ;
271 }
272
273
274 /***
275 * Get's whether or not this tuples's length is indefinite.
276 *
277 * @return whether or not this tuple's length is indefinite
278 */
279 public boolean isIndefinite()
280 {
281 return length == Length.INDEFINITE ;
282 }
283
284
285 /***
286 * Get's whether or not this tuple terminates an indefinite constructed
287 * tuple. This means that length == 0 && isPrimitive = true && id == 0
288 * and the type class is universal.
289 *
290 * @return whether or not this node's length is indefinite
291 */
292 public boolean isIndefiniteTerminator()
293 {
294 return isPrimitive && id == 0 && length <= 0 &&
295 typeClass.equals( TypeClass.UNIVERSAL ) ;
296 }
297
298
299 /***
300 * Gets whether or not this TLV tuple is primitive or constructed.
301 *
302 * @return true if it is primitive, false if it is constructed
303 */
304 public boolean isPrimitive()
305 {
306 return isPrimitive ;
307 }
308
309
310 /***
311 * Gets the value length for this TLV Tuple.
312 *
313 * @return the length in bytes of the value field for this TLV tuple
314 */
315 public int getLength()
316 {
317 return length ;
318 }
319
320
321
322 public void setLength( int length )
323 {
324 this.length = length;
325 }
326
327
328 /***
329 * Gets the BER TLV TypeClass for this TLV Tuple.
330 *
331 * @return the BER TLV TypeClass for this TLV Tuple
332 */
333 public TypeClass getTypeClass()
334 {
335 return typeClass ;
336 }
337
338
339 /***
340 * Gets the last chunk read for the value field (V-part) for this TLV Tuple.
341 *
342 * @return the last valueChunk field for this TLV Tuple
343 */
344 public ByteBuffer getLastValueChunk()
345 {
346 return valueChunk ;
347 }
348
349
350 /***
351 * Sets the value representing the last chunk read or the last chunch to
352 * write.
353 *
354 * @param buf the last chunk as a buffer
355 */
356 public void setLastValueChunk( ByteBuffer buf )
357 {
358 this.valueChunk = buf;
359 }
360
361
362 /***
363 * Gets the total size of this TLV tuple in bytes. This includes the
364 * length of the tag field, the length of the length field and the length
365 * of the value feild.
366 *
367 * @return the total TLV size in bytes
368 */
369 public int size()
370 {
371 if ( this.length == Length.INDEFINITE )
372 {
373 return getTagLength() + getLengthLength() ;
374 }
375 else
376 {
377 return getTagLength() + getLengthLength() + length ;
378 }
379 }
380
381
382
383
384
385
386
387 /***
388 * Clears the values of this tuple.
389 */
390 public void clear()
391 {
392 this.id = 0 ;
393 this.index = 0 ;
394 this.rawTag = 0 ;
395 this.isPrimitive = true ;
396 this.length = Length.UNDEFINED ;
397 this.typeClass = TypeClass.APPLICATION ;
398 this.valueChunk = EMPTY_BUFFER ;
399 this.valueIndex = Length.UNDEFINED ;
400 }
401
402
403 /***
404 * Does not take into account the value, index or the valueIndex values when
405 * checking for equality. Technically if both are being constructed by
406 * the decoder then they should only be equal when these values are equal
407 * because the tag, length or value would not be correct. Plus since this
408 * is a chunking tuple the valueChunk means nothing with respect to the
409 * final value.
410 *
411 * @see java.lang.Object#equals(java.lang.Object)
412 */
413 public boolean equals( Object o )
414 {
415 if ( o == this )
416 {
417 return true ;
418 }
419
420 if ( o instanceof Tuple )
421 {
422 Tuple t = ( Tuple ) o ;
423
424 if ( t.id != id )
425 {
426 return false ;
427 }
428
429 if ( t.isPrimitive != isPrimitive )
430 {
431 return false ;
432 }
433
434 if ( t.length != length )
435 {
436 return false ;
437 }
438
439 if ( t.typeClass != typeClass )
440 {
441 return false ;
442 }
443
444 return true ;
445 }
446
447 return false ;
448 }
449
450
451
452
453
454
455 public Object clone()
456 {
457 Tuple t = new Tuple() ;
458 t.id = id ;
459 t.rawTag = rawTag ;
460 t.isPrimitive = isPrimitive ;
461 t.typeClass = typeClass ;
462 t.length = length ;
463
464
465
466
467
468
469
470 ByteBuffer bb = valueChunk ;
471 ByteBuffer cloned = ByteBuffer.allocate( bb.capacity() ) ;
472 int oldPos = bb.position() ;
473 bb.rewind() ;
474 cloned.put( bb ) ;
475 cloned.limit( bb.limit() ) ;
476 bb.position( oldPos ) ;
477 cloned.rewind() ;
478 t.valueChunk = cloned ;
479
480
481 t.index = index ;
482 t.valueIndex = valueIndex ;
483
484 return t ;
485 }
486
487
488
489
490
491
492
493 /***
494 * If this is a primitive TLV then the valueBytes argument is used to
495 * produce an encoded image of this TLV. If it is constructed then
496 * only the TL part of the tuple is encoded leaving the value to be encoded
497 * by the set of child TLVs.
498 *
499 * @todo this should produce chunking output and needs to be removed from
500 * here actually and made into a standalone encoder. You give it a buffer
501 * and it fills it as much as it can remembering where the encode stopped.
502 * Hence it is stateful as expected from the statemachine.
503 *
504 * @return partial encoded image if constructed or complete TLV if primitive
505 */
506 public ByteBuffer toEncodedBuffer( List valueChunks )
507 {
508 ByteBuffer octets = null ;
509 int tagLength = getTagLength() ;
510 int lengthLength = getLengthLength() ;
511 int total = tagLength + lengthLength ;
512
513 if ( isPrimitive )
514 {
515 total += length ;
516 }
517
518 octets = ByteBuffer.allocate( total ) ;
519 setTag( octets, tagLength ) ;
520 setLength( octets, lengthLength ) ;
521
522 if ( isPrimitive )
523 {
524 for ( int ii = 0; ii < valueChunks.size(); ii++ )
525 {
526 octets.put( ( ByteBuffer ) valueChunks.get(ii) ) ;
527 }
528 }
529
530 return ( ByteBuffer ) octets.flip() ;
531 }
532
533
534 /***
535 * Sets the tag section within the buffer.
536 *
537 * @param octets the buffer to set the tag in
538 * @param tagLength the length of the tag section
539 */
540 public void setTag( ByteBuffer octets, int tagLength )
541 {
542 if ( tagLength >= 6 )
543 {
544 throw new IllegalArgumentException( "cannot support id's as large "
545 + "as " + id + " unless we start using longs for the id" ) ;
546 }
547
548 byte octet = ( byte ) typeClass.getValue() ;
549 int i = octets.position();
550
551 if ( ! isPrimitive )
552 {
553 octet |= BIT_5;
554 }
555
556 if ( id < 31 )
557 {
558 octets.put( ( byte ) ( octet | (id & Tag.SHORT_MASK ) ) ) ;
559 return;
560
561 }
562 else
563 {
564 octets.put( ( byte ) ( octet | Tag.SHORT_MASK ) ) ;
565 i++;
566 }
567
568 switch ( tagLength - 1) {
569 case 5 :
570 octets.put( ( byte ) ( ( ( id >> 21 ) & Tag.LONG_MASK ) | BIT_7 ) ) ;
571 i++;
572
573
574 case 4:
575 octets.put( ( byte ) ( ( ( id >> 21 ) & Tag.LONG_MASK ) | BIT_7 ) ) ;
576 i++;
577
578
579 case 3 :
580 octets.put( ( byte ) ( ( ( id >> 14 ) & Tag.LONG_MASK ) | BIT_7 ) ) ;
581 i++;
582
583
584 case 2 :
585 octets.put( ( byte ) ( ( ( id >> 7 ) & Tag.LONG_MASK ) | BIT_7 ) ) ;
586 i++;
587
588
589 case 1 :
590 octets.put( ( byte ) ( id & Tag.LONG_MASK ) ) ;
591 break;
592 }
593
594
595 return ;
596 }
597
598
599 /***
600 * Sets the value length of this Tuple.
601 *
602 * @param length the length of this tuple's value.
603 * @see Tuple#size() to get the entire determinate length of tuple
604 */
605 public void setValueLength( int length )
606 {
607 this.length = length;
608 }
609
610
611 /***
612 * Sets the length bytes.
613 *
614 * @param octets the byte [] to set length in
615 * @param lengthBytes the number bytes for the length section
616 */
617 public void setLength( ByteBuffer octets, int lengthBytes )
618 {
619 if ( lengthBytes >= 6 )
620 {
621 throw new IllegalArgumentException( "cannot support lengths larger "
622 + "than a max integer using " + lengthBytes
623 + " bytes unless we start using longs or BigIntegers for "
624 + "the length" ) ;
625 }
626
627 if ( length == Length.INDEFINITE )
628 {
629 octets.put( ( byte ) BIT_7 ) ;
630 return ;
631 }
632 else if ( lengthBytes == 1 )
633 {
634 octets.put( ( byte ) length ) ;
635 return ;
636 }
637 else
638 {
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657 octets.put( ( byte ) ( BIT_7 | ( lengthBytes - 1 ) ) ) ;
658 }
659
660
661
662 for ( int ii = 0, shift = (lengthBytes-2)<<3; ii <= lengthBytes-2; ii++, shift -= 8 )
663 {
664 octets.put( octets.position() + ii, ( byte ) ( ( ( 0xff << shift ) & length ) >> shift ) );
665 }
666
667 octets.position( octets.position() + lengthBytes - 1 );
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705 }
706
707
708 /***
709 * Gets the length in bytes of the tag section for this TLV tuple.
710 *
711 * @return the length in bytes of the tag section for this TLV tuple
712 */
713 public int getTagLength()
714 {
715 if ( id < 31 )
716 {
717 return 1 ;
718 }
719 else if ( id < BIT_6 )
720 {
721 return 2 ;
722 }
723
724 else if ( id < BIT_13 )
725 {
726 return 3 ;
727 }
728 else if ( id < BIT_20 )
729 {
730 return 4 ;
731 }
732 else if ( id < BIT_27 )
733 {
734 return 5 ;
735 }
736
737 throw new IllegalArgumentException( "cannot support id's larger than "
738 + id + " unless we start using longs for the id" ) ;
739 }
740
741
742 /***
743 * Gets the length in bytes of the length section of this TLV Tuple.
744 *
745 * @return the length in bytes of the length section
746 */
747 public int getLengthLength()
748 {
749 if ( length == Length.INDEFINITE )
750 {
751 return 1 ;
752 }
753
754 if ( length < 0 )
755 {
756 throw new IllegalArgumentException( "integer overflow makes id "
757 + "negative with a value of " + id
758 + " - unless we start using longs for"
759 + " the id there you've hit a limitation" ) ;
760 }
761 else if ( length < BIT_7 )
762 {
763 return 1 ;
764 }
765 else if ( length < 256 )
766 {
767 return 2 ;
768 }
769 else if ( length < BIT_15 )
770 {
771 return 3 ;
772 }
773 else if ( length < BIT_23 )
774 {
775 return 4 ;
776 }
777 else
778 {
779 return 5 ;
780 }
781 }
782 }