Browse Source

implement decode

d3nmas3n0 1 year ago
commit
a6bff8017c
8 changed files with 646 additions and 0 deletions
  1. 93 0
      codex.go
  2. 85 0
      codex_bitmap.go
  3. 60 0
      codex_lvar.go
  4. 46 0
      codex_string.go
  5. 26 0
      codex_test.go
  6. 13 0
      go.mod
  7. 10 0
      go.sum
  8. 313 0
      iso8583.yaml

+ 93 - 0
codex.go

@@ -0,0 +1,93 @@
+package codex
+
+import (
+  "fmt"
+  "gopkg.in/yaml.v3"
+  "os"
+)
+
+type Codex interface {
+  init(cfg map[string]interface{})
+
+  ID() string
+  Decode(data []byte, pos int, value interface{}) (int, error)
+}
+
+type CodexStruct struct {
+  id      string
+  fields  []Codex 
+}
+
+
+func LoadCodex(fileName string) Codex {
+	data, err := os.ReadFile(fileName)
+	if err != nil {
+    panic(fmt.Sprintf("Error read config %s: %s", fileName, err))
+	}
+  m := make(map[string]interface{})
+  err = yaml.Unmarshal(data, &m)
+  return parseType(m)
+}
+
+func parseType(cfg map[string]interface{}) Codex {
+  var c Codex
+  if ctype, ok := cfg["type"].(string); ok {
+    switch ctype {
+    case "struct":
+      c = &CodexStruct{}
+    case "string":
+      c = &CodexString{}
+    case "bitmap":
+      c = &CodexBitmap{}
+    case "llvar":
+      c = &CodexLVar{PacketLength: 2}
+    case "lllvar":
+      c = &CodexLVar{PacketLength: 3}
+    default:
+      panic(fmt.Sprintf("Unknown type [%s]", ctype))
+    }
+  } else if cfg["type"] == nil {
+    c = &CodexString{}
+  } else {
+    panic(fmt.Sprintf("Invalid Codex.Type %+v", cfg))
+  }
+  c.init(cfg)
+  return c
+}
+
+func (c *CodexStruct) init(cfg map[string]interface{}) {
+  if v, ok := cfg["id"].(string); ok {
+    c.id = v
+  } else {
+    panic(fmt.Sprintf("Invalid CodexStruct.ID %+v", cfg))
+  }
+  if v, ok := cfg["fields"].([]interface{}); ok {
+    c.fields = make([]Codex, len(v))
+    for i,f := range v {
+      if fm, ok := f.(map[string]interface{}); ok {
+        c.fields[i] = parseType(fm)
+      } else {
+        panic(fmt.Sprintf("Invalid field %d %+v", i, f))
+      }
+    }
+  } else {
+    panic(fmt.Sprintf("Invalid CodexStruct.Fields %+v", cfg))
+  }
+}
+
+func (c *CodexStruct) ID() string {
+  return c.id
+}
+
+func (c *CodexStruct) Decode(data []byte, pos int, value interface{}) (int, error) {
+  ppos := pos
+  var err error
+  for _, f := range c.fields {
+    ppos, err = f.Decode(data, ppos, value)
+    if err != nil {
+      return pos, err
+    }
+  }
+  return ppos, nil
+}
+

+ 85 - 0
codex_bitmap.go

@@ -0,0 +1,85 @@
+package codex
+
+import (
+  "fmt"
+  "strconv"
+)
+
+type CodexBitmap struct {
+  id      string
+  fields  []Codex 
+}
+
+func (c *CodexBitmap) init(cfg map[string]interface{}) {
+  if v, ok := cfg["id"].(string); ok {
+    c.id = v
+  } else {
+    panic(fmt.Sprintf("Invalid CodexBitmap.ID %+v", cfg))
+  }
+  if v, ok := cfg["fields"].([]interface{}); ok {
+    c.fields = make([]Codex, len(v))
+    for i,f := range v {
+      if fm, ok := f.(map[string]interface{}); ok {
+        c.fields[i] = parseType(fm)
+      } else {
+        panic(fmt.Sprintf("Invalid field %d %+v", i, f))
+      }
+    }
+  } else {
+    panic(fmt.Sprintf("Invalid CodexBitmap.Fields %+v", cfg))
+  }
+}
+
+func (c *CodexBitmap) ID() string {
+  return c.id
+}
+
+func (c *CodexBitmap) Decode(data []byte, pos int, value interface{}) (int, error) {
+  ppos := pos + 16
+  if len(data) < ppos {
+    return pos, fmt.Errorf("incomplete packet %d %d", len(data), ppos)
+  }
+  var err error
+  bi := 0
+  var bitmap [128]bool
+  for i := pos; i<ppos; i++ {
+    bbi, err := strconv.ParseInt(string(data[i:i+1]), 16, 32)
+    if err != nil {
+      panic(err)
+    }
+    ba := int64(8)
+    for j := 1; j <= 4; j++ {
+      bitmap[bi] = (bbi & ba) != 0
+      ba >>= 1
+      bi++
+    }
+  }
+  if bitmap[0] {
+    ppos1 := ppos
+    ppos = ppos + 16
+    if len(data) < ppos {
+      return pos, fmt.Errorf("incomplete packet %d %d", len(data), ppos)
+    }
+    for i := ppos1; i<ppos; i++ {
+      bbi, err := strconv.ParseInt(string(data[i:i+1]), 16, 32)
+      if err != nil {
+        panic(err)
+      }
+      ba := int64(8)
+      for j := 1; j <= 4; j++ {
+        bitmap[bi] = (bbi & ba) != 0
+        ba >>= 1
+        bi++
+      }
+    }
+  }
+  for i := 0; i < 127; i++ {
+    if bitmap[i+1] {
+      ppos, err = c.fields[i].Decode(data, ppos, value)
+      if err != nil {
+        return pos, err
+      }
+    }
+  }
+  return ppos, nil
+}

+ 60 - 0
codex_lvar.go

@@ -0,0 +1,60 @@
+package codex
+
+import (
+  "fmt"
+  "strconv"
+)
+
+type CodexLVar struct {
+  id           string
+  PacketLength int
+  length        int
+}
+
+func (c *CodexLVar) init(cfg map[string]interface{}) {
+  if v, ok := cfg["id"].(string); ok {
+    c.id = v
+  } else {
+    panic(fmt.Sprintf("Invalid CodexLVar.ID %+v", cfg))
+  }
+  if v, ok := cfg["length"].(int); ok {
+    c.length = v
+  } else if cfg["length"] == nil {
+    c.length = 0
+  } else {
+    panic(fmt.Sprintf("Invalid CodexLVar.Length %+v", cfg))
+  }
+}
+
+func (c *CodexLVar) ID() string {
+  return c.id
+}
+
+func (c *CodexLVar) decode(data []byte, pos int) (string, int, error) {
+  ppos := pos + c.PacketLength
+  if len(data) < ppos {
+    return "", pos, fmt.Errorf("incomplete packet field %s %d < %d", c.id, len(data), ppos)
+  }
+  plen, err := strconv.Atoi(string(data[pos:ppos]))
+  if err != nil {
+    return "", pos, fmt.Errorf("Invalid lvar %s length [%s] %s", c.id, string(data[pos:ppos]), err)
+  }
+  ppos1 := ppos
+  ppos = ppos + plen
+  if len(data) < ppos {
+    return "", pos, fmt.Errorf("incomplete packet field %s %d < %d", c.id, len(data), ppos)
+  }
+  return string(data[ppos1:ppos]), ppos, nil
+}
+
+func (c *CodexLVar) Decode(data []byte, pos int, value interface{}) (int, error) {
+  var err error
+  if vm, ok := value.(map[string]interface{}); ok {
+    vm[c.id], pos, err = c.decode(data, pos)
+    if err != nil {
+      return pos, err
+    }
+    return pos, nil
+  }
+  panic(fmt.Sprintf("Invalid value type %+v", value))
+}

+ 46 - 0
codex_string.go

@@ -0,0 +1,46 @@
+package codex
+
+import (
+  "fmt"
+)
+
+type CodexString struct {
+  id      string
+  length  int
+}
+
+func (c *CodexString) init(cfg map[string]interface{}) {
+  if v, ok := cfg["id"].(string); ok {
+    c.id = v
+  } else {
+    panic(fmt.Sprintf("Invalid CodexString.ID %+v", cfg))
+  }
+  if v, ok := cfg["length"].(int); ok {
+    c.length = v
+  } else {
+    panic(fmt.Sprintf("Invalid CodexString.Length %+v", cfg))
+  }
+}
+func (c *CodexString) ID() string {
+  return c.id
+}
+
+func (c *CodexString) decode(data []byte, pos int) (string, int, error) {
+  ppos := pos + c.length
+  if len(data) >= ppos {
+    return string(data[pos:ppos]), ppos, nil
+  }
+  return "", pos, fmt.Errorf("incomplete packet field %s %d < %d", c.id, len(data), ppos)
+}
+
+func (c *CodexString) Decode(data []byte, pos int, value interface{}) (int, error) {
+  var err error
+  if vm, ok := value.(map[string]interface{}); ok {
+    vm[c.id], pos, err = c.decode(data, pos)
+    if err != nil {
+      return pos, err
+    }
+    return pos, nil
+  }
+  panic(fmt.Sprintf("Invalid value type %+v", value))
+}

+ 26 - 0
codex_test.go

@@ -0,0 +1,26 @@
+package codex
+
+import (
+  "testing"
+  "github.com/stretchr/testify/assert"
+)
+
+func Test_iso8583_decode(t *testing.T) {
+  codex := LoadCodex("iso8583.yaml")
+  t.Logf("Config: %v", codex)
+  msg := "0800822000000000000004000000000000020623135417005592001005REQ 1"
+  data := make(map[string]interface{})
+  pos, err := codex.Decode([]byte(msg), 0, data)
+  assert.Nil(t, err, "decode err")
+  assert.Equal(t, 63, pos, "decode pos")
+  result := map[string]interface{}{
+    "F000": "0800",
+    "F007": "0623135417",
+    "F011": "005592",
+    "F070": "001",
+    "F127": "REQ 1",
+  }
+  assert.Equal(t, result, data, "decode data")
+}
+
+

+ 13 - 0
go.mod

@@ -0,0 +1,13 @@
+module code.senomas.com/seno/codex
+
+go 1.19
+
+require (
+	github.com/stretchr/testify v1.8.4
+	gopkg.in/yaml.v3 v3.0.1
+)
+
+require (
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+)

+ 10 - 0
go.sum

@@ -0,0 +1,10 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 313 - 0
iso8583.yaml

@@ -0,0 +1,313 @@
+id: iso8583
+type: struct
+fields:
+- id: F000
+  length: 4
+  encoding: ISO 8859-8
+- id: bitmap
+  type: bitmap
+  fields:
+  - id: F002
+    type: llvar
+  - id: F003
+    length: 6
+  - id: F004
+    length: 12
+  - id: F005
+    length: 12
+  - id: F006
+    length: 12
+  - id: F007
+    length: 10
+  - id: F008
+    length: 8
+  - id: F009
+    length: 8
+  - id: F010
+    length: 8
+  - id: F011
+    length: 6
+    min-length: 1
+  - id: F012
+    length: 6
+  - id: F013
+    length: 4
+  - id: F014
+    length: 4
+  - id: F015
+    length: 4
+  - id: F016
+    length: 4
+  - id: F017
+    length: 4
+  - id: F018
+    length: 4
+  - id: F019
+    length: 3
+  - id: F020
+    length: 3
+  - id: F021
+    length: 3
+  - id: F022
+    length: 3
+  - id: F023
+    length: 3
+  - id: F024
+    length: 3
+  - id: F025
+    length: 2
+  - id: F026
+    length: 2
+  - id: F027
+    length: 1
+  - id: F028
+    length: 8
+  - id: F029
+    length: 9
+  - id: F030
+    length: 9
+  - id: F031
+    length: 9
+  - id: F032
+    length: 11
+    type: llvar
+  - id: F033
+    length: 11
+    type: llvar
+  - id: F034
+    length: 28
+    type: llvar
+  - id: F035
+    length: 37
+    type: llvar
+  - id: F036
+    length: 104
+    type: lllvar
+  - id: F037
+    length: 12
+  - id: F038
+    length: 6
+  - id: F039
+    length: 2
+  - id: F040
+    length: 3
+  - id: F041
+    length: 8
+  - id: F042
+    length: 15
+  - id: F043
+    length: 40
+  - id: F044
+    length: 25
+    type: llvar
+  - id: F045
+    length: 76
+    type: llvar
+  - id: F046
+    length: 999
+    type: lllvar
+  - id: F047
+    length: 999
+    type: lllvar
+  - id: F048
+    length: 999
+    type: lllvar
+  - id: F049
+    length: 3
+  - id: F050
+    length: 3
+  - id: F051
+    length: 3
+  - id: F052
+    length: 16
+  - id: F053
+    length: 18
+  - id: F054
+    length: 999
+    type: lllvar
+  - id: F055
+    length: 999
+    type: lllvar
+  - id: F056
+    length: 999
+    type: lllvar
+  - id: F057
+    length: 999
+    type: lllvar
+  - id: F058
+    length: 999
+    type: lllvar
+  - id: F059
+    length: 999
+    type: lllvar
+  - id: F060
+    length: 999
+    type: lllvar
+  - id: F061
+    length: 999
+    type: lllvar
+  - id: F062
+    length: 999
+    type: lllvar
+  - id: F063
+    length: 999
+    type: lllvar
+  - id: F064
+    length: 16
+  - id: F065
+    length: 16
+  - id: F066
+    length: 1
+  - id: F067
+    length: 2
+  - id: F068
+    length: 3
+  - id: F069
+    length: 3
+  - id: F070
+    length: 3
+  - id: F071
+    length: 4
+  - id: F072
+    length: 4
+  - id: F073
+    length: 6
+  - id: F074
+    length: 10
+  - id: F075
+    length: 10
+  - id: F076
+    length: 10
+  - id: F077
+    length: 10
+  - id: F078
+    length: 10
+  - id: F079
+    length: 10
+  - id: F080
+    length: 10
+  - id: F081
+    length: 10
+  - id: F082
+    length: 14
+  - id: F083
+    length: 14
+  - id: F084
+    length: 14
+  - id: F085
+    length: 14
+  - id: F086
+    length: 15
+  - id: F087
+    length: 15
+  - id: F088
+    length: 15
+  - id: F089
+    length: 15
+  - id: F090
+    length: 42
+  - id: F091
+    length: 1
+  - id: F092
+    length: 2
+  - id: F093
+    length: 5
+  - id: F094
+    length: 7
+  - id: F095
+    length: 42
+  - id: F096
+    length: 8
+  - id: F097
+    length: 16
+  - id: F098
+    length: 25
+  - id: F099
+    length: 11
+    type: llvar
+  - id: F100
+    length: 11
+    type: llvar
+  - id: F101
+    length: 11
+    type: llvar
+  - id: F102
+    length: 28
+    type: llvar
+  - id: F103
+    length: 28
+    type: llvar
+  - id: F104
+    length: 100
+    type: lllvar
+  - id: F105
+    length: 999
+    type: lllvar
+  - id: F106
+    length: 999
+    type: lllvar
+  - id: F107
+    length: 999
+    type: lllvar
+  - id: F108
+    length: 999
+    type: lllvar
+  - id: F109
+    length: 999
+    type: lllvar
+  - id: F110
+    length: 999
+    type: lllvar
+  - id: F111
+    length: 999
+    type: lllvar
+  - id: F112
+    length: 999
+    type: lllvar
+  - id: F113
+    length: 11
+    type: llvar
+  - id: F114
+    length: 999
+    type: lllvar
+  - id: F115
+    length: 999
+    type: lllvar
+  - id: F116
+    length: 999
+    type: lllvar
+  - id: F117
+    length: 999
+    type: lllvar
+  - id: F118
+    length: 999
+    type: lllvar
+  - id: F119
+    length: 999
+    type: lllvar
+  - id: F120
+    length: 999
+    type: lllvar
+  - id: F121
+    length: 999
+    type: lllvar
+  - id: F122
+    length: 999
+    type: lllvar
+  - id: F123
+    length: 999
+    type: lllvar
+  - id: F124
+    length: 255
+    type: lllvar
+  - id: F125
+    length: 50
+    type: lllvar
+  - id: F126
+    length: 6
+    type: lllvar
+  - id: F127
+    length: 11
+    type: lllvar
+  - id: F128
+    length: 16