@@ -554,111 +554,136 @@ private fun Iterable<ByteArray>.flatten(): ByteArray {
554554 return output
555555}
556556
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
557584
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+ */
558595internal class StructuredCborParser (internal val element : CborElement , private val verifyObjectTags : Boolean ) :
559596 CborParserInterface {
560597
598+ private var layer: CborLayer = false to PeekingIterator (element)
561599
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 >? > ()
567600
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 >()
575602
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()
580608
581609 override fun end () {
582610 // Reset iterators when ending a structure
583- isMap = isMapStack.removeLast()
584- listIterator = layerStack.removeLast()
611+ layer = layerStack.removeLast()
585612 }
586613
587-
588614 override fun startArray (tags : ULongArray? ): Int {
589615 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} " )
592618 }
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
599624 }
600625
601626 override fun startMap (tags : ULongArray? ): Int {
602627 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} " )
605630 }
606- layerStack + = listIterator
607- isMapStack + = isMap
608- isMap = true
631+ layerStack + = layer
609632
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
614639 }
615640
616641 override fun nextNull (tags : ULongArray? ): Nothing? {
617642 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} " )
620645 }
621646 return null
622647 }
623648
624649 override fun nextBoolean (tags : ULongArray? ): Boolean {
625650 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} " )
628653 }
629- return (currentElement as CborBoolean ).value
654+ return (layer. currentElement as CborBoolean ).value
630655 }
631656
632657 override fun nextNumber (tags : ULongArray? ): Long {
633658 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} " )
638663 }
639664 }
640665
641666 override fun nextString (tags : ULongArray? ): String {
642667 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} " )
645670 }
646- return (currentElement as CborString ).value
671+ return (layer. currentElement as CborString ).value
647672 }
648673
649674 override fun nextByteString (tags : ULongArray? ): ByteArray {
650675 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} " )
653678 }
654- return (currentElement as CborByteString ).value
679+ return (layer. currentElement as CborByteString ).value
655680 }
656681
657682 override fun nextDouble (tags : ULongArray? ): Double {
658683 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} " )
662687 }
663688 }
664689
@@ -669,23 +694,27 @@ internal class StructuredCborParser(internal val element: CborElement, private v
669694 override fun nextTaggedStringOrNumber (): Triple <String ?, Long ?, ULongArray ?> {
670695 val tags = processTags(null )
671696
672- return when (val key = currentElement) {
697+ return when (val key = layer. currentElement) {
673698 is CborString -> Triple (key.value, null , tags)
674699 is CborPositiveInt -> Triple (null , key.value.toLong(), tags)
675700 is CborNegativeInt -> Triple (null , key.value, tags)
676701 else -> throw CborDecodingException (" Expected string or number key, got ${key?.let { it::class .simpleName } ? : " null" } " )
677702 }
678703 }
679704
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+ */
680710 private fun processTags (tags : ULongArray? ): ULongArray? {
681711
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
686715
687716 // 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
689718
690719 // Verify tags if needed
691720 if (verifyObjectTags) {
@@ -707,7 +736,6 @@ internal class StructuredCborParser(internal val element: CborElement, private v
707736
708737 override fun skipElement (tags : ULongArray? ) {
709738 // Process tags but don't do anything with the element
710- // TODO check for maps
711739 processTags(tags)
712740 }
713741}
0 commit comments