123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- 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
- }
|