2016-12-12 8 views
1

別のアプリケーションからのバイナリメッセージをいくつか逆シリアル化する必要があります。私はrestruct.ioを使用したいが、メッセージ構造のいくつかのフィールドは "非標準"のビット数(5ビット、3ビット、... 10ビット...)を使用する。非標準サイズのフィールドを逆シリアル化する方法は?

このタイプの構造体を処理する方法はありますか?私は何の成功もなく時間を探していますので、どんな助けも非常に歓迎されるでしょう。事前

おかげで私は私の質問を明確にする例を与えることをしようとウィル。コードを考える:

package main 

import (
    "encoding/binary" 
    "fmt" 

    restruct "gopkg.in/restruct.v1" 
) 

type MessageType uint8 

const (
    MessageTypeOne MessageType = iota + 1 
    MessageTypeTwo 
    MessageTypeThree 
) 

// Message is the data to deserialize from the binary stream 
type Message struct { 
    Length  uint32  `struct:"uint32"` // message size in bytes (including length) 
    Type  MessageType `struct:"uint8"` 
    Version uint8  `struct:"uint8:4"` // Just need 4 bits 
    Subversion uint8  `struct:"uint8:2"` // just need 2 bits 
    Optional uint8  `struct:"uint8:1"` // just one bit --> '1' means next field is NOT present 
    NodeName string  `` 
    ANumber uint16  `struct:"uint16:10"` // just need 10 bits 
} 

// (length(4)+type(1)+(version(4bits)+Subversion(2bits)+Optional(1bit))) = 6 bytes 
// need 32bit alignment 
func main() { 
    var inStream = []byte{0x08, // just 8 bytes needed 
     0x01,  // messge type = MessageTypeOne 
     0x4a,  // Version=0100 Subversion=10 Optional=1 ANumber = 0 (MSB bit) 
     0x00, 0x60, // ANumber(000 0000 011) Padding = 0 0000 for 32 bits alignment 
    } 
    var msg Message 

    err := restruct.Unpack(inStream, binary.BigEndian, &msg) 
    if err != nil { 
     panic(err) 
    } 
    fmt.Println(msg) 
    // Expected: 
    // msg.Length = 8 
    // msg.Type = 1 
    // msg.Version = 4 
    // msg.Subversion = 2 
    // msg.Optional = 1 
    // msg.NodeName = "" 
    // msg.ANumber = 3 
} 

私は...これは私の質問を明確にします

希望をTCP接続からあるinStreamを受信すると、バイナリデータをデシリアライズし、期待値とのメッセージ構造体を得ることになるでしょう。

再び感謝;)

+5

がそれを処理するためのいくつかの方法がありますが、我々は何を知っている必要はありませあなたが援助を必要としているかどうかです。あなたがしていることの例を挙げてください。 – JimB

答えて

0

を... 。まだ完全にテストが、動作しているようです...

は一度テストしたプルリクエストを送信しようとします...もちろん

func (e *encoder) writeBits(f field, inBuf []byte) { 

    var inputLength uint8 = uint8(len(inBuf)) 

    if f.BitSize == 0 { 
     // Having problems with complex64 type ... so we asume we want to read all 
     //f.BitSize = uint8(f.Type.Bits()) 
     f.BitSize = 8 * inputLength 
    } 

    // destPos: Destination position (in the result) of the first bit in the first byte 
    var destPos uint8 = 8 - e.bitCounter 

    // originPos: Original position of the first bit in the first byte 
    var originPos uint8 = f.BitSize % 8 
    if originPos == 0 { 
     originPos = 8 
    } 

    // numBytes: number of complete bytes to hold the result 
    var numBytes uint8 = f.BitSize/8 

    // numBits: number of remaining bits in the first non-complete byte of the result 
    var numBits uint8 = f.BitSize % 8 

    // number of positions we have to shift the bytes to get the result 
    var shift uint8 
    if originPos > destPos { 
     shift = originPos - destPos 
    } else { 
     shift = destPos - originPos 
    } 
    shift = shift % 8 

    var inputInitialIdx uint8 = inputLength - numBytes 
    if numBits > 0 { 
     inputInitialIdx = inputInitialIdx - 1 
    } 

    if originPos < destPos { 
     // shift left 
     carry := func(idx uint8) uint8 { 
      if (idx + 1) < inputLength { 
       return (inBuf[idx+1] >> (8 - shift)) 
      } 
      return 0x00 

     } 
     mask := func(idx uint8) uint8 { 
      if idx == 0 { 
       return (0x01 << destPos) - 1 
      } 
      return 0xFF 
     } 
     var idx uint8 = 0 
     for inIdx := inputInitialIdx; inIdx < inputLength; inIdx++ { 
      e.buf[idx] |= ((inBuf[inIdx] << shift) | carry(inIdx)) & mask(idx) 
      idx++ 
     } 

    } else { 
     // originPos >= destPos => shift right 
     var idx uint8 = 0 
     // carry : is a little bit tricky in this case because of the first case 
     // when idx == 0 and there is no carry at all 
     carry := func(idx uint8) uint8 { 
      if idx == 0 { 
       return 0x00 
      } 
      return (inBuf[idx-1] << (8 - shift)) 
     } 
     mask := func(idx uint8) uint8 { 
      if idx == 0 { 
       return (0x01 << destPos) - 1 
      } 
      return 0xFF 
     } 
     inIdx := inputInitialIdx 
     for ; inIdx < inputLength; inIdx++ { 
      //note: Should the mask be done BEFORE the OR with carry? 
      e.buf[idx] |= ((inBuf[inIdx] >> shift) | carry(inIdx)) & mask(idx) 

      idx++ 
     } 
     if ((e.bitCounter + f.BitSize) % 8) > 0 { 
      e.buf[idx] |= carry(inIdx) 
     } 
    } 

    //now we should update buffer and bitCounter 
    e.bitCounter = (e.bitCounter + f.BitSize) % 8 

    // move the head to the next non-complete byte used 
    headerUpdate := func() uint8 { 
     if (e.bitCounter == 0) && ((f.BitSize % 8) != 0) { 
      return (numBytes + 1) 
     } 
     return numBytes 
    } 

    e.buf = e.buf[headerUpdate():] 

    return 
} 
1

このカスタム構造体パッキングを実装する一般的なパッケージはおそらくありませんが、あなたは簡単に、各フィールドに必要なだけのビットを抽出する独自のメソッドを作成することができます。

func (m *Message) UnmarshalBinary(data []byte) error { 
    m.Length = binary.BigEndian.Uint32(data[:4]) 

    if int(m.Length) > len(data) { 
     return fmt.Errorf("not enough bytes") 
    } 

    m.Type = MessageType(data[4]) 

    m.Version = data[5] >> 4 
    m.Subversion = data[5] >> 2 & 0x03 
    m.Optional = data[5] >> 1 & 0x01 

    // move the index for ANumber back if there's an optional string 
    idx := 6 
    if m.Optional == 0 { 
     // remove the last two bytes for ANumber 
     end := int(m.Length) - 2 
     m.NodeName = string(data[6:end]) 
     idx = end 
    } 

    m.ANumber = uint16(data[idx]&0xc0)<<2 | uint16(data[idx]&0x3f<<2|data[idx+1]>>6) 
    return nil 

}

あなたはもちろん、範囲外のインデックスを作成するとき、このパニックをさせるのではなく、エラーを返すために、よりバウンドチェックを追加することができます。

私はあなたの定義と一致するように、わずかにあなたのinStreamスライスを修正し、あなたはここでの出力例を見ることができます:私はビットフィールドで作業できるようにするrestruct.ioのためにいくつかのパッチに取り組んできたhttps://play.golang.org/p/FoNoazluOF

+0

あなたの答えをありがとう...良いコードそれは私が今やっている方法に私の道を設定します。 – thamurath

関連する問題