2016-10-20 2 views
2

다음 인코딩 구조가 있습니다. 크기 필드가 벡터 데이터 바로 앞에 있으면 vector()로 벡터를 인코딩 할 수 있다는 것을 알고 있습니다. 그러나 여기서 벡터 크기를 인코딩하는 필드는 인접하지 않습니다.인코딩 벡터 길이 필드가 벡터에 인접하지 않습니다.

case class Item(
    address: Int, 
    size: Int, 
) 
case class Header { 
    // lots of other fields before 
    numOfItems: Int, 
    // lots of other fields after 
} 
case class Outer(
    hdr: Header, 
    items: Vector[] 
) 

아우터 디코딩 OK이다

Header.numOfItems는 비트 벡터로부터 판독하고 항목 vectorOfN로 생성된다 (제공 (hdr.numOfItems, Item.codec))의

인코딩 바깥 쪽이 문제입니다 :

인코딩 할 때 numOfItem을 items.length에서 가져오고 싶습니다. 아이템 벡터가 업데이트되거나 "콜백 인코딩 전"과 같은 것으로 numOfItems에 추가 코드를 설정할 수 있다는 것을 알고 있습니다.

더 멋진 해결책이있는 것이 문제입니다. 나에게 Header.numOfItemsOuter.items.length으로 중복되므로 이상적으로 인코더 만 numOfItems에 대해 알아야합니다.

답변

3

당신은 consume()을 사용하여 코덱을 구축 시도하고 Outer 객체 구축없이 시작할 수 : outerExpandedC.encode(OuterExpanded(-1, -1, Vector(Item(1,2), Item(3,4)))) 반환

Successful(BitVector(224 bits, 
    0xffffffff00000002fffffffe00000001000000020000000300000004)) 
      ^ ^ ^  ^-------^-> First Item 
       |-1  |  |-2 
         |Vector length inserted between the two header fields 

그 후, 당신 :

case class OuterExpanded(
    fieldBefore: Int, // Field before number of items in the binary encoding 
    fieldAdter: Int, // Field after number of items in the binary encoding 
    items: Vector[Item] // Encoded items 
) 

// Single Item codec 
def itemC: Codec[Item] = (int32 :: int32).as[Item] 

def outerExpandedC: Codec[OuterExpanded] = ( 
    int32 ::       // Field before count 
    int32.consume(c =>    // Item count 
     int32 ::      // Field after count 
     vectorOfN(provide(c), itemC)) // 'consume' (use and forget) the count 
    (_.tail.head.length)    // provide the length when encoding 
).as[OuterExpanded] 

상기 정의 된 바와을, 당신은 인코딩 할 때 다음을 얻을 xmap()Codec[OuterExpanded]은 다른 헤더 필드를 함께 묶어 자신의 개체로 만들 수 있습니다. 나는 볼품없는 '이종 목록과 완전히 익숙하지 않아요하지만,

이 아마 적용 할 수 있습니다
def outerC: Codec[Outer] = 
    outerExpandedC.xmap(_.toOuter,_.expand) 

case class OuterExpanded(fieldBefore: Int, fieldAfter: Int, items: Vector[Item]) { 
    def toOuter = Outer(Hdr(fieldBefore,fieldAfter), items) 
} 

case class Outer(header: Hdr, items: Vector[Item]) { 
    def expand = OuterExpanded(header.beforeField1, header.beforeField1, items) 
} 

더 복잡한 경우 - 또는 HList -하고있을 수 있습니다 : 즉 (이 변환 Outer에 대한 방법과 OuterExpanded 추가) 위의 예에서 _.tail.head.length을 호출하는 대신 벡터의 길이에 도달하는 더 좋은 방법입니다. 특히 인코딩 된 값의 개수 이후에 둘 이상의 필드가있는 경우 더욱 그렇습니다.

또한, Codec scaladoc 유용 사업자 나는 다음과 같은 코드를 내놓았다 이전의 답변에 따라

+0

확인. "Atomic ..."솔루션과 비교했을 때 약간의 반복이 있었지만 "상태 비 저장"코덱을 사용하는 장점이 있습니다. 아직 더 나은 해결책이 없다면 그것이 받아 들여진 대답이라고 말할 수 있습니다. –

0

을 발견 할 수있는 좋은 장소입니다. 위의 트릭 양식과 AtomicInteger를 사용하여 벡터 크기를 유지했습니다.

import java.util.concurrent.atomic.AtomicInteger 
import scala.Vector 
import org.scalatest._ 
import scodec._ 
import scodec.Attempt._ 
import scodec.codecs._ 
import scodec.bits._ 

object SomeStructure { 
    case class Item(
    address: Int, 
    size: Int) 

    def itemC: Codec[Item] = (int32 :: int32).as[Item] 

    case class Hdr(
    beforeField1: Int, 
    // vectorSize would be here 
    afterField1: Int) 
    // vectorSize is an "in" param when encoding and an "out" param when decoding 
    def hdrC(vectorSize: AtomicInteger): Codec[Hdr] = 
    (int32 :: 
     int32.consume(c => { 
     vectorSize.set(c); 
     int32 
     })((i) => vectorSize.get)).as[Hdr] 

    case class Outer(
    hdr: Hdr, 
    var items: Vector[Item]) 

    def outerC() = { 
    // when decoding the length is in this atomic integer 
    // when encoding it is set before 
    val c = new AtomicInteger(-1) 
    (hdrC(c) :: lazily(vectorOfN(provide(c.get), itemC))) 
     .xmapc(identity)((g) => { c.set(g.tail.head.length); g }) 
    }.as[Outer] 
} 

import SomeStructure._ 
class SomeStructureSpec extends FlatSpec with Matchers { 
    val bv = hex"ffffffff00000002ffffffff00000001000000020000000300000004".bits 
    val v = Vector(Item(1, 2), Item(3, 4)) 
    val bv2 = hex"ffffffff00000003ffffffff000000010000000200000003000000040000000500000006".bits 
    val v2 = Vector(Item(1, 2), Item(3, 4), Item(5, 6)) 
    val o = Outer(Hdr(-1, -1), v) 

    "outerC" should "encode" in { 
    o.items = v 
    outerC.encode(o) shouldBe Successful(bv) 
    o.items = v2 
    outerC.encode(o) shouldBe Successful(bv2) 
    } 
    it should "decode" in { 
    o.items = v 
    outerC.decode(bv) shouldBe Successful(DecodeResult(o, BitVector.empty)) 
    o.items = v2 
    outerC.decode(bv2) shouldBe Successful(DecodeResult(o, BitVector.empty)) 
    } 
} 
+0

왜 원자 적 정수가 필요한지 이해할 수 없다. 제안 된 예제에서 작동하지 않는 것이 있는가? – Shastick

+0

구조체를 평평하게하고 싶지 않았기 때문에 AtomicInteger가 필요합니다. 인코더는 수업을 작성하는 방법을 지시해서는 안됩니다. –

+0

알았어, 알았어. AtomicInteger를 사용하지 않고도 원하는 것을 얻을 수 있습니다. xmap()을 사용하여 다른 방법으로 '코덱 [외부]'에 대한 답변을 업데이트했습니다. – Shastick