@@ -47,6 +47,8 @@ type UnmarshalerBitmap interface {
47
47
Bits () (m map [int ]bool , err error )
48
48
}
49
49
50
+ var errStructFieldNonExistent = errors .New ("non existent struct field" )
51
+
50
52
// Unmarshal parses the ISO-8583 data and stores the result
51
53
// in the struct pointed to by v. If v is nil or not a pointer to a struct,
52
54
// Unmarshal returns an error.
@@ -59,8 +61,8 @@ type UnmarshalerBitmap interface {
59
61
// returns the amount of bytes consumed from original message. If unused bytes remain from input
60
62
// its not considerate an error.
61
63
// If an error is encountered a counter with consumed bytes up to the moment is returned.
62
- func Unmarshal (data []byte , v interface {}) (int , error ) { // TODO: Check duplicated fieds
63
- input := reflect .ValueOf (v )
64
+ func Unmarshal (data []byte , v interface {}) (int , error ) {
65
+ strctInput := reflect .ValueOf (v )
64
66
// bitnapN works like an index that allows to know which fields are already
65
67
// mapped to a bitmap. For example: If bitmap = 10 means that all fields from 1 to 10
66
68
// are contemplated by already unmarshaled bitmaps and the next bitmap should consider
@@ -69,77 +71,36 @@ func Unmarshal(data []byte, v interface{}) (int, error) { // TODO: Check duplica
69
71
70
72
// Because only each implementation of iso8583.Unmarshaler know how many bytes it needs to consume
71
73
// and can potentially only know it while reading the field raw data; all currently un-read bytes
72
- // are given to the fields which return how many bytes from the message it consumed, value that is
73
- // subtracted from unconsumedBytes.
74
- unconsumedBytes := make ([]byte , len (data ))
75
- copy (unconsumedBytes , data )
76
-
77
- // consumed returns the amount of consumed bytes
78
- consumed := func () int {
79
- return len (data ) - len (unconsumedBytes )
80
- }
81
-
82
- // consume reduces unconsumedBytes by the indicated amount with pointer safety.
83
- consume := func (m int , f string ) error {
84
- if m > len (unconsumedBytes ) {
85
- // This error can only be caused by bad unmarshal implementations
86
- return fmt .Errorf ("iso8583.unmarshal: Unmarshaler from field %s returned a n higher than unconsumed " +
87
- "bytes" , f )
88
- }
89
- unconsumedBytes = unconsumedBytes [m :]
90
- return nil
91
- }
92
-
93
- // mustLen returns the capacity of a field ignoring all possible errors.
94
- mustLen := func (f string ) int {
95
- _ , tag , _ := getUnmarshaler (input , f )
96
- l , _ := tag .lenINT ()
97
- return l
98
- }
74
+ // are given to the fields which return how many bytes from the message it consumed.
75
+ buffer := newUnmarshalBuffer (data )
99
76
100
77
// Only pointer to structs can be unmarshaled.
101
- if ! isPointerToStruct (input ) {
102
- return consumed (), errors .New ("iso8583.unmarshal: interface input is not a pointer to a structure" )
103
- }
104
-
105
- // Unmarshal MTI.
106
- m , err := unmarshalField (input , _tagMTI , unconsumedBytes )
107
- if err != nil {
108
- return consumed (), err
78
+ if ! isPointerToStruct (strctInput ) {
79
+ return buffer .UntilNowConsumed (), errors .New ("iso8583.unmarshal: interface input is not a pointer to a structure" )
109
80
}
110
81
111
- if err := consume (m , _tagMTI ); err != nil {
112
- return consumed (), err
113
- }
114
-
115
- // Unmarshal first bitmap.
116
- m , err = unmarshalField (input , _tagBITMAP , unconsumedBytes )
82
+ representativeBits , err := readMessageHeader (buffer , strctInput )
117
83
if err != nil {
118
- return consumed (), err
84
+ return buffer . UntilNowConsumed (), err
119
85
}
120
86
121
- if err := consume ( m , _tagBITMAP ); err != nil {
122
- return consumed (), err
123
- }
87
+ // Obtain the length tag from first bitmap to know how many
88
+ // fields presences are indicated by him.
89
+ bitmapN += representativeBits
124
90
125
91
// Get Bits method from first bitmap type to know which fields
126
92
// to expect.
127
- firstFieldsMethod , err := getBitsMethod (input , _tagBITMAP )
93
+ firstFieldsMethod , err := getBitsMethod (strctInput , _tagBITMAP )
128
94
if err != nil {
129
- return consumed (), err
95
+ return buffer . UntilNowConsumed (), err
130
96
}
131
97
132
98
// Execute Bits method.
133
99
firstFieldsList , err := firstFieldsMethod ()
134
100
if err != nil {
135
- return consumed (), fmt .Errorf ("iso8583.unmarshal: failed reading first bitmap: %w" , err )
101
+ return buffer . UntilNowConsumed (), fmt .Errorf ("iso8583.unmarshal: failed reading first bitmap: %w" , err )
136
102
}
137
103
138
- // Obtain the length tag from first bitmap to know how many
139
- // fields presences are indicated by him.
140
- // These operation are safe because there were executed during unmarshaling.
141
- bitmapN += mustLen (_tagBITMAP )
142
-
143
104
// This block is needed because if we use the original "firstFieldList" variable, we would copy only the reference
144
105
// to the map and modify the value of the "bitmap" field.
145
106
fieldList := make (map [int ]bool )
@@ -157,64 +118,130 @@ func Unmarshal(data []byte, v interface{}) (int, error) { // TODO: Check duplica
157
118
}
158
119
159
120
// Unmarshal current field.
160
- m , err := unmarshalField (input , strconv .Itoa (n ), unconsumedBytes )
121
+ m , tagValues , err := unmarshalField (strctInput , strconv .Itoa (n ), buffer . Bytes () )
161
122
if err != nil {
162
- return consumed (), err
123
+ return buffer . UntilNowConsumed (), err
163
124
}
164
125
165
- if err := consume (m , "struct " + strconv .Itoa (n )); err != nil {
166
- return consumed (), err
126
+ if err := buffer . IncrementConsumedCounter (m , "struct " + strconv .Itoa (n )); err != nil {
127
+ return buffer . UntilNowConsumed (), err
167
128
}
168
129
169
130
// Check if current field is a bitmap.
170
- if bitsMethod , err := getBitsMethod (input , strconv .Itoa (n )); bitsMethod != nil && err == nil {
131
+ if bitsMethod , err := getBitsMethod (strctInput , strconv .Itoa (n )); bitsMethod != nil && err == nil {
171
132
// If bits method is present current field is a bitmap and the method is executed.
172
133
fieldsListExpansion , err := bitsMethod ()
173
134
if err != nil {
174
- return consumed (), fmt .Errorf ("iso8583.unmarshal: failed reading field %v bitmap: %w" , n , err )
135
+ return buffer . UntilNowConsumed (), fmt .Errorf ("iso8583.unmarshal: failed reading field %v bitmap: %w" , n , err )
175
136
}
176
137
177
138
// Expand current field list with obtained information from current field.
178
139
expandFieldList (fieldList , fieldsListExpansion , bitmapN )
179
140
180
141
// Obtain the length tag from first bitmap to know how many
181
142
// fields presences are indicated by him.
182
- // These operation are safe because there were executed during unmarshaling.
183
143
// The value is added to index of mapped fields.
184
- bitmapN += mustLen ( strconv . Itoa ( n ))
144
+ bitmapN += tagValues . Length
185
145
}
186
146
}
187
147
188
148
// Return the amount of consumed fields.
189
- return consumed (), nil
149
+ return buffer . UntilNowConsumed (), nil
190
150
}
191
151
192
- // unmarshalField and save the value. Returns the amount of consumed bytes.
193
- func unmarshalField (strct reflect.Value , fieldName string , bytes []byte ) (int , error ) {
194
- fieldInterface , tag , err := getUnmarshaler (strct , fieldName )
152
+ type unmarshalBuffer struct {
153
+ data []byte
154
+ placeholder int
155
+ }
156
+
157
+ func newUnmarshalBuffer (data []byte ) * unmarshalBuffer {
158
+ buffer := unmarshalBuffer {data : make ([]byte , len (data ))}
159
+ copy (buffer .data , data )
160
+ return & buffer
161
+ }
162
+
163
+ // incrementConsumed increments the placeholder considering the data length.
164
+ func (buffer * unmarshalBuffer ) IncrementConsumedCounter (n int , fieldName string ) error {
165
+ if len (buffer .data [buffer .placeholder :]) < n {
166
+ // This error can only be caused by bad unmarshal implementations
167
+ return fmt .Errorf ("iso8583.unmarshal: Unmarshaler from field %s returned a n higher than unconsumed " +
168
+ "bytes" , fieldName )
169
+ }
170
+
171
+ buffer .placeholder += n
172
+ return nil
173
+ }
174
+
175
+ // UntilNowConsumed returns the amount of until now consumed bytes
176
+ func (buffer * unmarshalBuffer ) UntilNowConsumed () int { return buffer .placeholder }
177
+
178
+ // Bytes returns a copy from the input data bytes remainder.
179
+ func (buffer * unmarshalBuffer ) Bytes () []byte {
180
+ data := make ([]byte , len (buffer .data [buffer .placeholder :]))
181
+ copy (data , buffer .data [buffer .placeholder :])
182
+ return data
183
+ }
184
+
185
+ // readMessageHeader reads the message MTI and the first bitmap.
186
+ // Returns the amount of representative bits of the bitmap.
187
+ func readMessageHeader (buffer * unmarshalBuffer , strct reflect.Value ) (int , error ) {
188
+ // Unmarshal MTI.
189
+ consumed , _ , err := unmarshalField (strct , _tagMTI , buffer .Bytes ())
195
190
if err != nil {
196
191
return 0 , err
197
192
}
198
193
199
- if fieldInterface == nil {
200
- return 0 , fmt .Errorf ("iso8583.unmarshal: unknown field in message '%v', cant resolve upcomming fields" ,
201
- fieldName )
194
+ if err := buffer .IncrementConsumedCounter (consumed , _tagMTI ); err != nil {
195
+ return 0 , err
202
196
}
203
197
204
- return executeUnmarshal ( fieldInterface , bytes , * tag )
198
+ return readFirstBitmap ( buffer , strct )
205
199
}
206
200
207
- // executeUnmarshal calls unmarshal method of objective, obtaining parameters from tags.
208
- // Returns consumed bytes from implementation.
209
- func executeUnmarshal (field Unmarshaler , b []byte , tag tags ) (int , error ) {
210
- // Transform length from string to int type.
211
- length , err := tag .lenINT ()
201
+ // readFirstBitmap reads the first bitmap and returns the amount of representative bits.
202
+ func readFirstBitmap (buffer * unmarshalBuffer , strct reflect.Value ) (int , error ) {
203
+ // Unmarshal first bitmap.
204
+ consumed , tagValues , err := unmarshalField (strct , _tagBITMAP , buffer .Bytes ())
212
205
if err != nil {
213
206
return 0 , err
214
207
}
215
208
209
+ if err := buffer .IncrementConsumedCounter (consumed , _tagBITMAP ); err != nil {
210
+ return 0 , err
211
+ }
212
+
213
+ return tagValues .Length , nil
214
+ }
215
+
216
+ // unmarshalField and save the value. Returns the amount of consumed bytes.
217
+ func unmarshalField (strct reflect.Value , fieldName string , bytes []byte ) (int , tags , error ) {
218
+ fieldValue , tag , err := searchStructField (strct , fieldName )
219
+ if err != nil {
220
+ if errors .Is (err , errStructFieldNonExistent ) {
221
+ err = fmt .Errorf ("unknown field in message '%v', cant resolve upcomming fields" ,
222
+ fieldName )
223
+ }
224
+
225
+ return 0 , tags {}, fmt .Errorf ("iso8583.unmarshal: %w" , err )
226
+ }
227
+
228
+ // founded field must implement Unmarshaler, otherwise an error is returned.
229
+ fieldInterface , isValidUnmarshaler := fieldValue .Interface ().(Unmarshaler )
230
+ if ! isValidUnmarshaler {
231
+ return 0 , tags {}, fmt .Errorf (
232
+ "iso8583.unmarshal: field %s is present but does not implement Unmarshaler interface" , fieldName )
233
+ }
234
+
235
+ consumed , err := executeUnmarshal (fieldInterface , bytes , tag )
236
+
237
+ return consumed , tag , err
238
+ }
239
+
240
+ // executeUnmarshal calls unmarshal method of objective, obtaining parameters from tags.
241
+ // Returns consumed bytes from implementation.
242
+ func executeUnmarshal (field Unmarshaler , b []byte , tag tags ) (int , error ) {
216
243
// Execute unmarshal.
217
- n , err := field .UnmarshalISO8583 (b , length , tag .Encoding )
244
+ n , err := field .UnmarshalISO8583 (b , tag . Length , tag .Encoding )
218
245
if err != nil {
219
246
return 0 , fmt .Errorf ("iso8583.unmarshal: cant unmarshal field %v: %w" , tag .Field , err )
220
247
}
@@ -223,11 +250,12 @@ func executeUnmarshal(field Unmarshaler, b []byte, tag tags) (int, error) {
223
250
return n , nil
224
251
}
225
252
226
- // getUnmarshaler iterates over target struct until it finds a field that matches with the field name.
227
- // If field is not found all values are returned in nil.
228
- func getUnmarshaler (v reflect.Value , n string ) (Unmarshaler , * tags , error ) {
253
+ // Returns the reflect value of the indicated field with its tags.
254
+ // If v isn't a struct this function panics.
255
+ // Returns errStructFieldNonExistent if the field is non existing.
256
+ func searchStructField (v reflect.Value , n string ) (reflect.Value , tags , error ) {
229
257
var strct reflect.Value
230
- var tag * tags
258
+ var tag tags
231
259
232
260
// Obtain underlying struct.
233
261
for strct = v ; strct .Kind () == reflect .Ptr ; {
@@ -237,44 +265,48 @@ func getUnmarshaler(v reflect.Value, n string) (Unmarshaler, *tags, error) {
237
265
// Iterate over struct until a field match with the tag name.
238
266
var vField reflect.Value
239
267
for index , strctType := 0 , strct .Type (); index < strctType .NumField (); index ++ {
240
- if tags := readTags (strctType .Field (index ).Tag ); tags != nil && tags .Field == n {
268
+ if structFieldValue , structFieldTags , err := getStructFieldData (strct , index ); structFieldTags .Field == n {
269
+ if err != nil {
270
+ return reflect.Value {}, tags {}, err
271
+ }
272
+
241
273
if ! reflect .ValueOf (vField ).IsZero () {
242
- return nil , nil , fmt .Errorf ("iso8583.marshal: field %v is repeteated in struct" , n )
274
+ return reflect. Value {}, tags {} , fmt .Errorf ("field %v is repeteated in struct" , n )
243
275
}
244
276
245
- vField = strct .Field (index )
246
- if vField .Kind () != reflect .Ptr {
247
- vField = vField .Addr ()
277
+ if structFieldValue .Kind () != reflect .Ptr {
278
+ structFieldValue = structFieldValue .Addr ()
248
279
}
249
280
250
- tag = tags
281
+ vField = structFieldValue
282
+ tag = structFieldTags
251
283
}
252
284
}
253
285
254
286
// If field is not found all values are returned nil.
255
287
if reflect .ValueOf (vField ).IsZero () {
256
288
// Field not in struct
257
- return nil , nil , nil
289
+ return reflect. Value {}, tags {}, errStructFieldNonExistent
258
290
}
259
291
260
- // founded field must implement Unmarshaler, otherwise an error is returned.
261
- field , isValidUnmarshaler := vField .Interface ().(Unmarshaler )
262
- if ! isValidUnmarshaler {
263
- return nil , nil , fmt .Errorf (
264
- "iso8583.unmarshal: %s is present and its a struct but does not implement Unmarshaler interface" , n )
265
- }
292
+ return vField , tag , nil
293
+ }
294
+
295
+ // getStructFieldData returns the specified struct field data using the struct index.
296
+ func getStructFieldData (parentStructValue reflect.Value , index int ) (reflect.Value , tags , error ) {
297
+ // search tags from current field, if there aren't field is ignored.
298
+ tags , err := searchTags (parentStructValue .Type ().Field (index ))
266
299
267
- // Return field data.
268
- return field , tag , nil
300
+ return parentStructValue .Field (index ), tags , err
269
301
}
270
302
271
303
// getBitsMethod searches a iso8583.UnmarshalerBitmap implementation and returns it Bits method.
272
304
func getBitsMethod (strct reflect.Value , fieldName string ) (func () (map [int ]bool , error ), error ) {
273
305
// Reuse getUnmarshaler because iso8583.Unmarshaler contains iso8583.UnmarshalerBitmap
274
- bitmapUnmarshaler , _ , _ := getUnmarshaler (strct , fieldName )
306
+ bitmapUnmarshaler , _ , _ := searchStructField (strct , fieldName )
275
307
276
308
// Field must implement iso8583.UnmarshalerBitmap.
277
- bitmap , ok := bitmapUnmarshaler .(UnmarshalerBitmap )
309
+ bitmap , ok := bitmapUnmarshaler .Interface (). (UnmarshalerBitmap )
278
310
if ! ok {
279
311
return nil , fmt .Errorf ("iso8583.unmarshal: %s field is present but does not implement UnmarshalerBitmap" ,
280
312
fieldName )
@@ -285,7 +317,12 @@ func getBitsMethod(strct reflect.Value, fieldName string) (func() (map[int]bool,
285
317
}
286
318
287
319
func isPointerToStruct (v reflect.Value ) bool {
288
- return v .Kind () == reflect .Ptr && v .Elem ().Kind () == reflect .Struct
320
+ underlyingObj := v
321
+ for underlyingObj .Kind () == reflect .Ptr {
322
+ underlyingObj = underlyingObj .Elem ()
323
+ }
324
+
325
+ return v .Kind () == reflect .Ptr && underlyingObj .Kind () == reflect .Struct
289
326
}
290
327
291
328
func expandFieldList (list map [int ]bool , expansion map [int ]bool , bitmapN int ) map [int ]bool {
0 commit comments