package ndr
import (
"bufio"
"fmt"
"io"
"reflect"
"strings"
)
const (
TagConformant = "conformant"
TagVarying = "varying"
TagPointer = "pointer"
TagPipe = "pipe"
)
type Decoder struct {
r *bufio .Reader
size int
ch CommonHeader
ph PrivateHeader
conformantMax []uint32
s interface {}
current []string
}
type deferedPtr struct {
v reflect .Value
tag reflect .StructTag
}
func NewDecoder (r io .Reader ) *Decoder {
dec := new (Decoder )
dec .r = bufio .NewReader (r )
dec .r .Peek (int (commonHeaderBytes ))
dec .size = dec .r .Buffered ()
return dec
}
func (dec *Decoder ) Decode (s interface {}) error {
dec .s = s
err := dec .readCommonHeader ()
if err != nil {
return err
}
err = dec .readPrivateHeader ()
if err != nil {
return err
}
_, err = dec .r .Discard (4 )
if err != nil {
return Errorf ("unable to process byte stream: %v" , err )
}
return dec .process (s , reflect .StructTag ("" ))
}
func (dec *Decoder ) process (s interface {}, tag reflect .StructTag ) error {
err := dec .scanConformantArrays (s , tag )
if err != nil {
return err
}
var localDef []deferedPtr
err = dec .fill (s , tag , &localDef )
if err != nil {
return Errorf ("could not decode: %v" , err )
}
for _ , p := range localDef {
err = dec .process (p .v , p .tag )
if err != nil {
return fmt .Errorf ("could not decode deferred referent: %v" , err )
}
}
return nil
}
func (dec *Decoder ) scanConformantArrays (s interface {}, tag reflect .StructTag ) error {
err := dec .conformantScan (s , tag )
if err != nil {
return fmt .Errorf ("failed to scan for embedded conformant arrays: %v" , err )
}
for i := range dec .conformantMax {
dec .conformantMax [i ], err = dec .readUint32 ()
if err != nil {
return fmt .Errorf ("could not read preceding conformant max count index %d: %v" , i , err )
}
}
return nil
}
func (dec *Decoder ) conformantScan (s interface {}, tag reflect .StructTag ) error {
ndrTag := parseTags (tag )
if ndrTag .HasValue (TagPointer ) {
return nil
}
v := getReflectValue (s )
switch v .Kind () {
case reflect .Struct :
for i := 0 ; i < v .NumField (); i ++ {
err := dec .conformantScan (v .Field (i ), v .Type ().Field (i ).Tag )
if err != nil {
return err
}
}
case reflect .String :
if !ndrTag .HasValue (TagConformant ) {
break
}
dec .conformantMax = append (dec .conformantMax , uint32 (0 ))
case reflect .Slice :
if !ndrTag .HasValue (TagConformant ) {
break
}
d , t := sliceDimensions (v .Type ())
for i := 0 ; i < d ; i ++ {
dec .conformantMax = append (dec .conformantMax , uint32 (0 ))
}
if t .Kind () == reflect .String {
dec .conformantMax = append (dec .conformantMax , uint32 (0 ))
}
}
return nil
}
func (dec *Decoder ) isPointer (v reflect .Value , tag reflect .StructTag , def *[]deferedPtr ) (bool , error ) {
ndrTag := parseTags (tag )
if ndrTag .HasValue (TagPointer ) {
p , err := dec .readUint32 ()
if err != nil {
return true , fmt .Errorf ("could not read pointer: %v" , err )
}
ndrTag .delete (TagPointer )
if p != 0 {
*def = append (*def , deferedPtr {v , ndrTag .StructTag ()})
}
return true , nil
}
return false , nil
}
func getReflectValue(s interface {}) (v reflect .Value ) {
if r , ok := s .(reflect .Value ); ok {
v = r
} else {
if reflect .ValueOf (s ).Kind () == reflect .Ptr {
v = reflect .ValueOf (s ).Elem ()
}
}
return
}
func (dec *Decoder ) fill (s interface {}, tag reflect .StructTag , localDef *[]deferedPtr ) error {
v := getReflectValue (s )
ptr , err := dec .isPointer (v , tag , localDef )
if err != nil {
return fmt .Errorf ("could not process struct field(%s): %v" , strings .Join (dec .current , "/" ), err )
}
if ptr {
return nil
}
switch v .Kind () {
case reflect .Struct :
dec .current = append (dec .current , v .Type ().Name ())
var unionTag reflect .Value
var unionField string
for i := 0 ; i < v .NumField (); i ++ {
fieldName := v .Type ().Field (i ).Name
dec .current = append (dec .current , fieldName )
structTag := v .Type ().Field (i ).Tag
ndrTag := parseTags (structTag )
if !unionTag .IsValid () {
unionTag = dec .isUnion (v .Field (i ), structTag )
} else {
if unionField == "" {
unionField , err = unionSelectedField (v , unionTag )
if err != nil {
return fmt .Errorf ("could not determine selected union value field for %s with discriminat" +
" tag %s: %v" , v .Type ().Name (), unionTag , err )
}
}
if ndrTag .HasValue (TagUnionField ) && fieldName != unionField {
dec .current = dec .current [:len (dec .current )-1 ]
continue
}
}
if v .Field (i ).Type ().Implements (reflect .TypeOf (new (RawBytes )).Elem ()) &&
v .Field (i ).Type ().Kind () == reflect .Slice && v .Field (i ).Type ().Elem ().Kind () == reflect .Uint8 {
structTag , err = addSizeToTag (v , v .Field (i ), structTag )
if err != nil {
return fmt .Errorf ("could not get rawbytes field(%s) size: %v" , strings .Join (dec .current , "/" ), err )
}
ptr , err := dec .isPointer (v .Field (i ), structTag , localDef )
if err != nil {
return fmt .Errorf ("could not process struct field(%s): %v" , strings .Join (dec .current , "/" ), err )
}
if !ptr {
err := dec .readRawBytes (v .Field (i ), structTag )
if err != nil {
return fmt .Errorf ("could not fill raw bytes struct field(%s): %v" , strings .Join (dec .current , "/" ), err )
}
}
} else {
err := dec .fill (v .Field (i ), structTag , localDef )
if err != nil {
return fmt .Errorf ("could not fill struct field(%s): %v" , strings .Join (dec .current , "/" ), err )
}
}
dec .current = dec .current [:len (dec .current )-1 ]
}
dec .current = dec .current [:len (dec .current )-1 ]
case reflect .Bool :
i , err := dec .readBool ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Uint8 :
i , err := dec .readUint8 ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Uint16 :
i , err := dec .readUint16 ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Uint32 :
i , err := dec .readUint32 ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Uint64 :
i , err := dec .readUint64 ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Int8 :
i , err := dec .readInt8 ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Int16 :
i , err := dec .readInt16 ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Int32 :
i , err := dec .readInt32 ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Int64 :
i , err := dec .readInt64 ()
if err != nil {
return fmt .Errorf ("could not fill %s: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .String :
ndrTag := parseTags (tag )
conformant := ndrTag .HasValue (TagConformant )
var s string
var err error
if conformant {
s , err = dec .readConformantVaryingString (localDef )
if err != nil {
return fmt .Errorf ("could not fill with conformant varying string: %v" , err )
}
} else {
s , err = dec .readVaryingString (localDef )
if err != nil {
return fmt .Errorf ("could not fill with varying string: %v" , err )
}
}
v .Set (reflect .ValueOf (s ))
case reflect .Float32 :
i , err := dec .readFloat32 ()
if err != nil {
return fmt .Errorf ("could not fill %v: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Float64 :
i , err := dec .readFloat64 ()
if err != nil {
return fmt .Errorf ("could not fill %v: %v" , v .Type ().Name (), err )
}
v .Set (reflect .ValueOf (i ))
case reflect .Array :
err := dec .fillFixedArray (v , tag , localDef )
if err != nil {
return err
}
case reflect .Slice :
if v .Type ().Implements (reflect .TypeOf (new (RawBytes )).Elem ()) && v .Type ().Elem ().Kind () == reflect .Uint8 {
err := dec .readRawBytes (v , tag )
if err != nil {
return fmt .Errorf ("could not fill raw bytes struct field(%s): %v" , strings .Join (dec .current , "/" ), err )
}
break
}
ndrTag := parseTags (tag )
conformant := ndrTag .HasValue (TagConformant )
varying := ndrTag .HasValue (TagVarying )
if ndrTag .HasValue (TagPipe ) {
err := dec .fillPipe (v , tag )
if err != nil {
return err
}
break
}
_ , t := sliceDimensions (v .Type ())
if t .Kind () == reflect .String && !ndrTag .HasValue (subStringArrayValue ) {
err := dec .readStringsArray (v , tag , localDef )
if err != nil {
return err
}
break
}
if conformant && varying {
err := dec .fillConformantVaryingArray (v , tag , localDef )
if err != nil {
return err
}
} else if !conformant && varying {
err := dec .fillVaryingArray (v , tag , localDef )
if err != nil {
return err
}
} else {
err := dec .fillConformantArray (v , tag , localDef )
if err != nil {
return err
}
}
default :
return fmt .Errorf ("unsupported type" )
}
return nil
}
func (dec *Decoder ) readBytes (n int ) ([]byte , error ) {
b := make ([]byte , n , n )
m , err := dec .r .Read (b )
if err != nil || m != n {
return b , fmt .Errorf ("error reading bytes from stream: %v" , err )
}
return b , nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .