blob: 5e2def2a829813cdcec5910d89d04488fb81750e [file] [log] [blame]
Scott Baker8487c5d2019-10-18 12:49:46 -07001package ndr
2
3import (
4 "errors"
5 "fmt"
6 "reflect"
7 "strconv"
8)
9
10// intFromTag returns an int that is a value in a struct tag key/value pair
11func intFromTag(tag reflect.StructTag, key string) (int, error) {
12 ndrTag := parseTags(tag)
13 d := 1
14 if n, ok := ndrTag.Map[key]; ok {
15 i, err := strconv.Atoi(n)
16 if err != nil {
17 return d, fmt.Errorf("invalid dimensions tag [%s]: %v", n, err)
18 }
19 d = i
20 }
21 return d, nil
22}
23
24// parseDimensions returns the a slice of the size of each dimension and type of the member at the deepest level.
25func parseDimensions(v reflect.Value) (l []int, tb reflect.Type) {
26 if v.Kind() == reflect.Ptr {
27 v = v.Elem()
28 }
29 t := v.Type()
30 if t.Kind() == reflect.Ptr {
31 t = t.Elem()
32 }
33 if t.Kind() != reflect.Array && t.Kind() != reflect.Slice {
34 return
35 }
36 l = append(l, v.Len())
37 if t.Elem().Kind() == reflect.Array || t.Elem().Kind() == reflect.Slice {
38 // contains array or slice
39 var m []int
40 m, tb = parseDimensions(v.Index(0))
41 l = append(l, m...)
42 } else {
43 tb = t.Elem()
44 }
45 return
46}
47
48// sliceDimensions returns the count of dimensions a slice has.
49func sliceDimensions(t reflect.Type) (d int, tb reflect.Type) {
50 if t.Kind() == reflect.Ptr {
51 t = t.Elem()
52 }
53 if t.Kind() == reflect.Slice {
54 d++
55 var n int
56 n, tb = sliceDimensions(t.Elem())
57 d += n
58 } else {
59 tb = t
60 }
61 return
62}
63
64// makeSubSlices is a deep recursive creation/initialisation of multi-dimensional slices.
65// Takes the reflect.Value of the 1st dimension and a slice of the lengths of the sub dimensions
66func makeSubSlices(v reflect.Value, l []int) {
67 ty := v.Type().Elem()
68 if ty.Kind() != reflect.Slice {
69 return
70 }
71 for i := 0; i < v.Len(); i++ {
72 s := reflect.MakeSlice(ty, l[0], l[0])
73 v.Index(i).Set(s)
74 // Are there more sub dimensions?
75 if len(l) > 1 {
76 makeSubSlices(v.Index(i), l[1:])
77 }
78 }
79 return
80}
81
82// multiDimensionalIndexPermutations returns all the permutations of the indexes of a multi-dimensional slice.
83// The input is a slice of integers that indicates the max size/length of each dimension
84func multiDimensionalIndexPermutations(l []int) (ps [][]int) {
85 z := make([]int, len(l), len(l)) // The zeros permutation
86 ps = append(ps, z)
87 // for each dimension, in reverse
88 for i := len(l) - 1; i >= 0; i-- {
89 ws := make([][]int, len(ps))
90 copy(ws, ps)
91 //create a permutation for each of the iterations of the current dimension
92 for j := 1; j <= l[i]-1; j++ {
93 // For each existing permutation
94 for _, p := range ws {
95 np := make([]int, len(p), len(p))
96 copy(np, p)
97 np[i] = j
98 ps = append(ps, np)
99 }
100 }
101 }
102 return
103}
104
105// precedingMax reads off the next conformant max value
106func (dec *Decoder) precedingMax() uint32 {
107 m := dec.conformantMax[0]
108 dec.conformantMax = dec.conformantMax[1:]
109 return m
110}
111
112// fillFixedArray establishes if the fixed array is uni or multi dimensional and then fills it.
113func (dec *Decoder) fillFixedArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error {
114 l, t := parseDimensions(v)
115 if t.Kind() == reflect.String {
116 tag = reflect.StructTag(subStringArrayTag)
117 }
118 if len(l) < 1 {
119 return errors.New("could not establish dimensions of fixed array")
120 }
121 if len(l) == 1 {
122 err := dec.fillUniDimensionalFixedArray(v, tag, def)
123 if err != nil {
124 return fmt.Errorf("could not fill uni-dimensional fixed array: %v", err)
125 }
126 return nil
127 }
128 // Fixed array is multidimensional
129 ps := multiDimensionalIndexPermutations(l[:len(l)-1])
130 for _, p := range ps {
131 // Get current multi-dimensional index to fill
132 a := v
133 for _, i := range p {
134 a = a.Index(i)
135 }
136 // fill with the last dimension array
137 err := dec.fillUniDimensionalFixedArray(a, tag, def)
138 if err != nil {
139 return fmt.Errorf("could not fill dimension %v of multi-dimensional fixed array: %v", p, err)
140 }
141 }
142 return nil
143}
144
145// readUniDimensionalFixedArray reads an array (not slice) from the byte stream.
146func (dec *Decoder) fillUniDimensionalFixedArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error {
147 for i := 0; i < v.Len(); i++ {
148 err := dec.fill(v.Index(i), tag, def)
149 if err != nil {
150 return fmt.Errorf("could not fill index %d of fixed array: %v", i, err)
151 }
152 }
153 return nil
154}
155
156// fillConformantArray establishes if the conformant array is uni or multi dimensional and then fills the slice.
157func (dec *Decoder) fillConformantArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error {
158 d, _ := sliceDimensions(v.Type())
159 if d > 1 {
160 err := dec.fillMultiDimensionalConformantArray(v, d, tag, def)
161 if err != nil {
162 return err
163 }
164 } else {
165 err := dec.fillUniDimensionalConformantArray(v, tag, def)
166 if err != nil {
167 return err
168 }
169 }
170 return nil
171}
172
173// fillUniDimensionalConformantArray fills the uni-dimensional slice value.
174func (dec *Decoder) fillUniDimensionalConformantArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error {
175 m := dec.precedingMax()
176 n := int(m)
177 a := reflect.MakeSlice(v.Type(), n, n)
178 for i := 0; i < n; i++ {
179 err := dec.fill(a.Index(i), tag, def)
180 if err != nil {
181 return fmt.Errorf("could not fill index %d of uni-dimensional conformant array: %v", i, err)
182 }
183 }
184 v.Set(a)
185 return nil
186}
187
188// fillMultiDimensionalConformantArray fills the multi-dimensional slice value provided from conformant array data.
189// The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this
190// method not to panic.
191func (dec *Decoder) fillMultiDimensionalConformantArray(v reflect.Value, d int, tag reflect.StructTag, def *[]deferedPtr) error {
192 // Read the max size of each dimensions from the ndr stream
193 l := make([]int, d, d)
194 for i := range l {
195 l[i] = int(dec.precedingMax())
196 }
197 // Initialise size of slices
198 // Initialise the size of the 1st dimension
199 ty := v.Type()
200 v.Set(reflect.MakeSlice(ty, l[0], l[0]))
201 // Initialise the size of the other dimensions recursively
202 makeSubSlices(v, l[1:])
203
204 // Get all permutations of the indexes and go through each and fill
205 ps := multiDimensionalIndexPermutations(l)
206 for _, p := range ps {
207 // Get current multi-dimensional index to fill
208 a := v
209 for _, i := range p {
210 a = a.Index(i)
211 }
212 err := dec.fill(a, tag, def)
213 if err != nil {
214 return fmt.Errorf("could not fill index %v of slice: %v", p, err)
215 }
216 }
217 return nil
218}
219
220// fillVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice.
221func (dec *Decoder) fillVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error {
222 d, t := sliceDimensions(v.Type())
223 if d > 1 {
224 err := dec.fillMultiDimensionalVaryingArray(v, t, d, tag, def)
225 if err != nil {
226 return err
227 }
228 } else {
229 err := dec.fillUniDimensionalVaryingArray(v, tag, def)
230 if err != nil {
231 return err
232 }
233 }
234 return nil
235}
236
237// fillUniDimensionalVaryingArray fills the uni-dimensional slice value.
238func (dec *Decoder) fillUniDimensionalVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error {
239 o, err := dec.readUint32()
240 if err != nil {
241 return fmt.Errorf("could not read offset of uni-dimensional varying array: %v", err)
242 }
243 s, err := dec.readUint32()
244 if err != nil {
245 return fmt.Errorf("could not establish actual count of uni-dimensional varying array: %v", err)
246 }
247 t := v.Type()
248 // Total size of the array is the offset in the index being passed plus the actual count of elements being passed.
249 n := int(s + o)
250 a := reflect.MakeSlice(t, n, n)
251 // Populate the array starting at the offset specified
252 for i := int(o); i < n; i++ {
253 err := dec.fill(a.Index(i), tag, def)
254 if err != nil {
255 return fmt.Errorf("could not fill index %d of uni-dimensional varying array: %v", i, err)
256 }
257 }
258 v.Set(a)
259 return nil
260}
261
262// fillMultiDimensionalVaryingArray fills the multi-dimensional slice value provided from varying array data.
263// The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this
264// method not to panic.
265func (dec *Decoder) fillMultiDimensionalVaryingArray(v reflect.Value, t reflect.Type, d int, tag reflect.StructTag, def *[]deferedPtr) error {
266 // Read the offset and actual count of each dimensions from the ndr stream
267 o := make([]int, d, d)
268 l := make([]int, d, d)
269 for i := range l {
270 off, err := dec.readUint32()
271 if err != nil {
272 return fmt.Errorf("could not read offset of dimension %d: %v", i+1, err)
273 }
274 o[i] = int(off)
275 s, err := dec.readUint32()
276 if err != nil {
277 return fmt.Errorf("could not read size of dimension %d: %v", i+1, err)
278 }
279 l[i] = int(s) + int(off)
280 }
281 // Initialise size of slices
282 // Initialise the size of the 1st dimension
283 ty := v.Type()
284 v.Set(reflect.MakeSlice(ty, l[0], l[0]))
285 // Initialise the size of the other dimensions recursively
286 makeSubSlices(v, l[1:])
287
288 // Get all permutations of the indexes and go through each and fill
289 ps := multiDimensionalIndexPermutations(l)
290 for _, p := range ps {
291 // Get current multi-dimensional index to fill
292 a := v
293 var os bool // should this permutation be skipped due to the offset of any of the dimensions?
294 for i, j := range p {
295 if j < o[i] {
296 os = true
297 break
298 }
299 a = a.Index(j)
300 }
301 if os {
302 // This permutation should be skipped as it is less than the offset for one of the dimensions.
303 continue
304 }
305 err := dec.fill(a, tag, def)
306 if err != nil {
307 return fmt.Errorf("could not fill index %v of slice: %v", p, err)
308 }
309 }
310 return nil
311}
312
313// fillConformantVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice.
314func (dec *Decoder) fillConformantVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error {
315 d, t := sliceDimensions(v.Type())
316 if d > 1 {
317 err := dec.fillMultiDimensionalConformantVaryingArray(v, t, d, tag, def)
318 if err != nil {
319 return err
320 }
321 } else {
322 err := dec.fillUniDimensionalConformantVaryingArray(v, tag, def)
323 if err != nil {
324 return err
325 }
326 }
327 return nil
328}
329
330// fillUniDimensionalConformantVaryingArray fills the uni-dimensional slice value.
331func (dec *Decoder) fillUniDimensionalConformantVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error {
332 m := dec.precedingMax()
333 o, err := dec.readUint32()
334 if err != nil {
335 return fmt.Errorf("could not read offset of uni-dimensional conformant varying array: %v", err)
336 }
337 s, err := dec.readUint32()
338 if err != nil {
339 return fmt.Errorf("could not establish actual count of uni-dimensional conformant varying array: %v", err)
340 }
341 if m < o+s {
342 return errors.New("max count is less than the offset plus actual count")
343 }
344 t := v.Type()
345 n := int(s)
346 a := reflect.MakeSlice(t, n, n)
347 for i := int(o); i < n; i++ {
348 err := dec.fill(a.Index(i), tag, def)
349 if err != nil {
350 return fmt.Errorf("could not fill index %d of uni-dimensional conformant varying array: %v", i, err)
351 }
352 }
353 v.Set(a)
354 return nil
355}
356
357// fillMultiDimensionalConformantVaryingArray fills the multi-dimensional slice value provided from conformant varying array data.
358// The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this
359// method not to panic.
360func (dec *Decoder) fillMultiDimensionalConformantVaryingArray(v reflect.Value, t reflect.Type, d int, tag reflect.StructTag, def *[]deferedPtr) error {
361 // Read the offset and actual count of each dimensions from the ndr stream
362 m := make([]int, d, d)
363 for i := range m {
364 m[i] = int(dec.precedingMax())
365 }
366 o := make([]int, d, d)
367 l := make([]int, d, d)
368 for i := range l {
369 off, err := dec.readUint32()
370 if err != nil {
371 return fmt.Errorf("could not read offset of dimension %d: %v", i+1, err)
372 }
373 o[i] = int(off)
374 s, err := dec.readUint32()
375 if err != nil {
376 return fmt.Errorf("could not read actual count of dimension %d: %v", i+1, err)
377 }
378 if m[i] < int(s)+int(off) {
379 m[i] = int(s) + int(off)
380 }
381 l[i] = int(s)
382 }
383 // Initialise size of slices
384 // Initialise the size of the 1st dimension
385 ty := v.Type()
386 v.Set(reflect.MakeSlice(ty, m[0], m[0]))
387 // Initialise the size of the other dimensions recursively
388 makeSubSlices(v, m[1:])
389
390 // Get all permutations of the indexes and go through each and fill
391 ps := multiDimensionalIndexPermutations(m)
392 for _, p := range ps {
393 // Get current multi-dimensional index to fill
394 a := v
395 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
396 for i, j := range p {
397 if j < o[i] || j >= l[i] {
398 os = true
399 break
400 }
401 a = a.Index(j)
402 }
403 if os {
404 // This permutation should be skipped as it is less than the offset for one of the dimensions.
405 continue
406 }
407 err := dec.fill(a, tag, def)
408 if err != nil {
409 return fmt.Errorf("could not fill index %v of slice: %v", p, err)
410 }
411 }
412 return nil
413}