@@ -554,111 +554,136 @@ private fun Iterable<ByteArray>.flatten(): ByteArray {
554
554
return output
555
555
}
556
556
557
+ /* *
558
+ * Iterator that keeps a reference to the current element and allows peeking at the next element.
559
+ * Works for single elements (where current is directly set to the element) and for collections (where current
560
+ * will be first set after `startMap` or `startArray`
561
+ */
562
+ private class PeekingIterator <T : Any >(private val iter : ListIterator <T >) : Iterator<T> by iter {
563
+
564
+ lateinit var current: T
565
+ private set
566
+
567
+ override fun next (): T = iter.next().also { current = it }
568
+
569
+ fun peek () = if (hasNext()) {
570
+ val next = iter.next()
571
+ iter.previous()
572
+ next
573
+ } else null
574
+
575
+ companion object {
576
+ operator fun invoke (single : CborElement ): PeekingIterator <CborElement > =
577
+ PeekingIterator (listOf (single).listIterator()).also { it.next() }
578
+ }
579
+ }
580
+ /* * Maps are a bit special for nullability checks, so we need to check whether we're in a map or not*/
581
+ private typealias CborLayer = Pair <Boolean , PeekingIterator <CborElement >>
582
+
583
+ private val CborLayer .isMap get() = first
557
584
585
+ /* * current element reference inside a layer. If the layer is a primitive then the current element is the layer itself*/
586
+ private val CborLayer .currentElement get() = second.current
587
+ private fun CborLayer.peek () = second.peek()
588
+ private fun CborLayer.hasNext () = second.hasNext()
589
+ private fun CborLayer.nextElement () = second.next()
590
+
591
+ /* *
592
+ * CBOR parser that operates on [CborElement] instead of bytes. Closely mirrors the baheviour of [CborParser], so the
593
+ * [CborDecoder] can remain largely unchanged.
594
+ */
558
595
internal class StructuredCborParser (internal val element : CborElement , private val verifyObjectTags : Boolean ) :
559
596
CborParserInterface {
560
597
598
+ private var layer: CborLayer = false to PeekingIterator (element)
561
599
562
- internal var currentElement = element
563
- private var listIterator: ListIterator <CborElement >? = null
564
- private var isMap = false
565
- private val isMapStack = ArrayDeque <Boolean >()
566
- private val layerStack = ArrayDeque <ListIterator <CborElement >? > ()
567
600
568
- override fun isNull (): Boolean {
569
- return if (isMap) {
570
- val isNull = listIterator!! .next() is CborNull
571
- listIterator!! .previous()
572
- isNull
573
- } else currentElement is CborNull
574
- }
601
+ private val layerStack = ArrayDeque <CborLayer >()
575
602
576
- override fun isEnd () = when {
577
- listIterator != null -> ! listIterator!! .hasNext()
578
- else -> false
579
- }
603
+ // map needs special treatment because keys and values are laid out as a list alternating between key and value to
604
+ // mirror the byte-layout of a cbor map.
605
+ override fun isNull () = if (layer.isMap) layer.peek() is CborNull else layer.currentElement is CborNull
606
+
607
+ override fun isEnd () = ! layer.hasNext()
580
608
581
609
override fun end () {
582
610
// Reset iterators when ending a structure
583
- isMap = isMapStack.removeLast()
584
- listIterator = layerStack.removeLast()
611
+ layer = layerStack.removeLast()
585
612
}
586
613
587
-
588
614
override fun startArray (tags : ULongArray? ): Int {
589
615
processTags(tags)
590
- if (currentElement !is CborList ) {
591
- throw CborDecodingException (" Expected array, got ${currentElement::class .simpleName} " )
616
+ if (layer. currentElement !is CborList ) {
617
+ throw CborDecodingException (" Expected array, got ${layer. currentElement::class .simpleName} " )
592
618
}
593
- isMapStack + = isMap
594
- layerStack + = listIterator
595
- isMap = false
596
- val list = currentElement as CborList
597
- listIterator = list.listIterator()
598
- return - 1 // just let the iterator run out of elements
619
+ layerStack + = layer
620
+ val list = layer.currentElement as CborList
621
+ layer = false to PeekingIterator (list.listIterator())
622
+ return list.size // we could just return -1 and let the current layer run out of elements to never run into inconsistencies
623
+ // if we do keep it like this, any inconsistencies serve as a canary for implementation bugs
599
624
}
600
625
601
626
override fun startMap (tags : ULongArray? ): Int {
602
627
processTags(tags)
603
- if (currentElement !is CborMap ) {
604
- throw CborDecodingException (" Expected map, got ${currentElement::class .simpleName} " )
628
+ if (layer. currentElement !is CborMap ) {
629
+ throw CborDecodingException (" Expected map, got ${layer. currentElement::class .simpleName} " )
605
630
}
606
- layerStack + = listIterator
607
- isMapStack + = isMap
608
- isMap = true
631
+ layerStack + = layer
609
632
610
- val map = currentElement as CborMap
611
- // zip key, value, key, value, ... pairs to mirror byte-layout of CBOR map
612
- listIterator = map.entries.flatMap { listOf (it.key, it.value) }.listIterator()
613
- return - 1 // just let the iterator run out of elements
633
+ val map = layer.currentElement as CborMap
634
+ // zip key, value, key, value, ... pairs to mirror byte-layout of CBOR map, so decoding this here works the same
635
+ // as decoding from bytes
636
+ layer = true to PeekingIterator (map.entries.flatMap { listOf (it.key, it.value) }.listIterator())
637
+ return map.size// we could just return -1 and let the current layer run out of elements to never run into inconsistencies
638
+ // if we do keep it like this, any inconsistencies serve as a canary for implementation bugs
614
639
}
615
640
616
641
override fun nextNull (tags : ULongArray? ): Nothing? {
617
642
processTags(tags)
618
- if (currentElement !is CborNull ) {
619
- throw CborDecodingException (" Expected null, got ${currentElement::class .simpleName} " )
643
+ if (layer. currentElement !is CborNull ) {
644
+ throw CborDecodingException (" Expected null, got ${layer. currentElement::class .simpleName} " )
620
645
}
621
646
return null
622
647
}
623
648
624
649
override fun nextBoolean (tags : ULongArray? ): Boolean {
625
650
processTags(tags)
626
- if (currentElement !is CborBoolean ) {
627
- throw CborDecodingException (" Expected boolean, got ${currentElement::class .simpleName} " )
651
+ if (layer. currentElement !is CborBoolean ) {
652
+ throw CborDecodingException (" Expected boolean, got ${layer. currentElement::class .simpleName} " )
628
653
}
629
- return (currentElement as CborBoolean ).value
654
+ return (layer. currentElement as CborBoolean ).value
630
655
}
631
656
632
657
override fun nextNumber (tags : ULongArray? ): Long {
633
658
processTags(tags)
634
- return when (currentElement) {
635
- is CborPositiveInt -> (currentElement as CborPositiveInt ).value.toLong()
636
- is CborNegativeInt -> (currentElement as CborNegativeInt ).value
637
- else -> throw CborDecodingException (" Expected number, got ${currentElement::class .simpleName} " )
659
+ return when (layer. currentElement) {
660
+ is CborPositiveInt -> (layer. currentElement as CborPositiveInt ).value.toLong()
661
+ is CborNegativeInt -> (layer. currentElement as CborNegativeInt ).value
662
+ else -> throw CborDecodingException (" Expected number, got ${layer. currentElement::class .simpleName} " )
638
663
}
639
664
}
640
665
641
666
override fun nextString (tags : ULongArray? ): String {
642
667
processTags(tags)
643
- if (currentElement !is CborString ) {
644
- throw CborDecodingException (" Expected string, got ${currentElement::class .simpleName} " )
668
+ if (layer. currentElement !is CborString ) {
669
+ throw CborDecodingException (" Expected string, got ${layer. currentElement::class .simpleName} " )
645
670
}
646
- return (currentElement as CborString ).value
671
+ return (layer. currentElement as CborString ).value
647
672
}
648
673
649
674
override fun nextByteString (tags : ULongArray? ): ByteArray {
650
675
processTags(tags)
651
- if (currentElement !is CborByteString ) {
652
- throw CborDecodingException (" Expected byte string, got ${currentElement::class .simpleName} " )
676
+ if (layer. currentElement !is CborByteString ) {
677
+ throw CborDecodingException (" Expected byte string, got ${layer. currentElement::class .simpleName} " )
653
678
}
654
- return (currentElement as CborByteString ).value
679
+ return (layer. currentElement as CborByteString ).value
655
680
}
656
681
657
682
override fun nextDouble (tags : ULongArray? ): Double {
658
683
processTags(tags)
659
- return when (currentElement) {
660
- is CborDouble -> (currentElement as CborDouble ).value
661
- else -> throw CborDecodingException (" Expected double, got ${currentElement::class .simpleName} " )
684
+ return when (layer. currentElement) {
685
+ is CborDouble -> (layer. currentElement as CborDouble ).value
686
+ else -> throw CborDecodingException (" Expected double, got ${layer. currentElement::class .simpleName} " )
662
687
}
663
688
}
664
689
@@ -669,23 +694,27 @@ internal class StructuredCborParser(internal val element: CborElement, private v
669
694
override fun nextTaggedStringOrNumber (): Triple <String ?, Long ?, ULongArray ?> {
670
695
val tags = processTags(null )
671
696
672
- return when (val key = currentElement) {
697
+ return when (val key = layer. currentElement) {
673
698
is CborString -> Triple (key.value, null , tags)
674
699
is CborPositiveInt -> Triple (null , key.value.toLong(), tags)
675
700
is CborNegativeInt -> Triple (null , key.value, tags)
676
701
else -> throw CborDecodingException (" Expected string or number key, got ${key?.let { it::class .simpleName } ? : " null" } " )
677
702
}
678
703
}
679
704
705
+ /* *
706
+ * Verify the current element's object tags and advance to the next element if inside a list/map.
707
+ * The reason this method mixes two behaviours is that decoding a primitive is invoked on a single element.
708
+ * `decodeElementIndex`, etc. is invoked on an iterable and there are key tags and value tags
709
+ */
680
710
private fun processTags (tags : ULongArray? ): ULongArray? {
681
711
682
- // If we're in a list, advance to the next element
683
- if (listIterator != null && listIterator!! .hasNext()) {
684
- currentElement = listIterator!! .next()
685
- }
712
+ // If we're in a list/map, advance to the next element
713
+ if (layer.hasNext()) layer.nextElement()
714
+ // if we're at a primitive, we only process tags
686
715
687
716
// Store collected tags for verification
688
- val collectedTags = if (currentElement.tags.isEmpty()) null else currentElement.tags
717
+ val collectedTags = if (layer. currentElement.tags.isEmpty()) null else layer. currentElement.tags
689
718
690
719
// Verify tags if needed
691
720
if (verifyObjectTags) {
@@ -707,7 +736,6 @@ internal class StructuredCborParser(internal val element: CborElement, private v
707
736
708
737
override fun skipElement (tags : ULongArray? ) {
709
738
// Process tags but don't do anything with the element
710
- // TODO check for maps
711
739
processTags(tags)
712
740
}
713
741
}
0 commit comments