package ndr import ( "errors" "fmt" "reflect" "strconv" ) // intFromTag returns an int that is a value in a struct tag key/value pair func intFromTag(tag reflect.StructTag, key string) (int, error) { ndrTag := parseTags(tag) d := 1 if n, ok := ndrTag.Map[key]; ok { i, err := strconv.Atoi(n) if err != nil { return d, fmt.Errorf("invalid dimensions tag [%s]: %v", n, err) } d = i } return d, nil } // parseDimensions returns the a slice of the size of each dimension and type of the member at the deepest level. func parseDimensions(v reflect.Value) (l []int, tb reflect.Type) { if v.Kind() == reflect.Ptr { v = v.Elem() } t := v.Type() if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { return } l = append(l, v.Len()) if t.Elem().Kind() == reflect.Array || t.Elem().Kind() == reflect.Slice { // contains array or slice var m []int m, tb = parseDimensions(v.Index(0)) l = append(l, m...) } else { tb = t.Elem() } return } // sliceDimensions returns the count of dimensions a slice has. func sliceDimensions(t reflect.Type) (d int, tb reflect.Type) { if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() == reflect.Slice { d++ var n int n, tb = sliceDimensions(t.Elem()) d += n } else { tb = t } return } // makeSubSlices is a deep recursive creation/initialisation of multi-dimensional slices. // Takes the reflect.Value of the 1st dimension and a slice of the lengths of the sub dimensions func makeSubSlices(v reflect.Value, l []int) { ty := v.Type().Elem() if ty.Kind() != reflect.Slice { return } for i := 0; i < v.Len(); i++ { s := reflect.MakeSlice(ty, l[0], l[0]) v.Index(i).Set(s) // Are there more sub dimensions? if len(l) > 1 { makeSubSlices(v.Index(i), l[1:]) } } return } // multiDimensionalIndexPermutations returns all the permutations of the indexes of a multi-dimensional slice. // The input is a slice of integers that indicates the max size/length of each dimension func multiDimensionalIndexPermutations(l []int) (ps [][]int) { z := make([]int, len(l), len(l)) // The zeros permutation ps = append(ps, z) // for each dimension, in reverse for i := len(l) - 1; i >= 0; i-- { ws := make([][]int, len(ps)) copy(ws, ps) //create a permutation for each of the iterations of the current dimension for j := 1; j <= l[i]-1; j++ { // For each existing permutation for _, p := range ws { np := make([]int, len(p), len(p)) copy(np, p) np[i] = j ps = append(ps, np) } } } return } // precedingMax reads off the next conformant max value func (dec *Decoder) precedingMax() uint32 { m := dec.conformantMax[0] dec.conformantMax = dec.conformantMax[1:] return m } // fillFixedArray establishes if the fixed array is uni or multi dimensional and then fills it. func (dec *Decoder) fillFixedArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { l, t := parseDimensions(v) if t.Kind() == reflect.String { tag = reflect.StructTag(subStringArrayTag) } if len(l) < 1 { return errors.New("could not establish dimensions of fixed array") } if len(l) == 1 { err := dec.fillUniDimensionalFixedArray(v, tag, def) if err != nil { return fmt.Errorf("could not fill uni-dimensional fixed array: %v", err) } return nil } // Fixed array is multidimensional ps := multiDimensionalIndexPermutations(l[:len(l)-1]) for _, p := range ps { // Get current multi-dimensional index to fill a := v for _, i := range p { a = a.Index(i) } // fill with the last dimension array err := dec.fillUniDimensionalFixedArray(a, tag, def) if err != nil { return fmt.Errorf("could not fill dimension %v of multi-dimensional fixed array: %v", p, err) } } return nil } // readUniDimensionalFixedArray reads an array (not slice) from the byte stream. func (dec *Decoder) fillUniDimensionalFixedArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { for i := 0; i < v.Len(); i++ { err := dec.fill(v.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of fixed array: %v", i, err) } } return nil } // fillConformantArray establishes if the conformant array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillConformantArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, _ := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalConformantArray(v, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalConformantArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalConformantArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalConformantArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { m := dec.precedingMax() n := int(m) a := reflect.MakeSlice(v.Type(), n, n) for i := 0; i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional conformant array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalConformantArray fills the multi-dimensional slice value provided from conformant array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalConformantArray(v reflect.Value, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the max size of each dimensions from the ndr stream l := make([]int, d, d) for i := range l { l[i] = int(dec.precedingMax()) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, l[0], l[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, l[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(l) for _, p := range ps { // Get current multi-dimensional index to fill a := v for _, i := range p { a = a.Index(i) } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil } // fillVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, t := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalVaryingArray(v, t, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalVaryingArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalVaryingArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { o, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of uni-dimensional varying array: %v", err) } s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not establish actual count of uni-dimensional varying array: %v", err) } t := v.Type() // Total size of the array is the offset in the index being passed plus the actual count of elements being passed. n := int(s + o) a := reflect.MakeSlice(t, n, n) // Populate the array starting at the offset specified for i := int(o); i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional varying array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalVaryingArray fills the multi-dimensional slice value provided from varying array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalVaryingArray(v reflect.Value, t reflect.Type, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the offset and actual count of each dimensions from the ndr stream o := make([]int, d, d) l := make([]int, d, d) for i := range l { off, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of dimension %d: %v", i+1, err) } o[i] = int(off) s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read size of dimension %d: %v", i+1, err) } l[i] = int(s) + int(off) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, l[0], l[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, l[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(l) for _, p := range ps { // Get current multi-dimensional index to fill a := v var os bool // should this permutation be skipped due to the offset of any of the dimensions? for i, j := range p { if j < o[i] { os = true break } a = a.Index(j) } if os { // This permutation should be skipped as it is less than the offset for one of the dimensions. continue } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil } // fillConformantVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillConformantVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, t := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalConformantVaryingArray(v, t, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalConformantVaryingArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalConformantVaryingArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalConformantVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { m := dec.precedingMax() o, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of uni-dimensional conformant varying array: %v", err) } s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not establish actual count of uni-dimensional conformant varying array: %v", err) } if m < o+s { return errors.New("max count is less than the offset plus actual count") } t := v.Type() n := int(s) a := reflect.MakeSlice(t, n, n) for i := int(o); i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional conformant varying array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalConformantVaryingArray fills the multi-dimensional slice value provided from conformant varying array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalConformantVaryingArray(v reflect.Value, t reflect.Type, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the offset and actual count of each dimensions from the ndr stream m := make([]int, d, d) for i := range m { m[i] = int(dec.precedingMax()) } o := make([]int, d, d) l := make([]int, d, d) for i := range l { off, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of dimension %d: %v", i+1, err) } o[i] = int(off) s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read actual count of dimension %d: %v", i+1, err) } if m[i] < int(s)+int(off) { m[i] = int(s) + int(off) } l[i] = int(s) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, m[0], m[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, m[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(m) for _, p := range ps { // Get current multi-dimensional index to fill a := v var os bool // should this permutation be skipped due to the offset of any of the dimensions or max is higher than the actual count being passed for i, j := range p { if j < o[i] || j >= l[i] { os = true break } a = a.Index(j) } if os { // This permutation should be skipped as it is less than the offset for one of the dimensions. continue } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil }