Skip to content

Commit

Permalink
NO-ISSUE Support custom data coding (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
linxGnu authored Mar 24, 2021
1 parent 188a1e1 commit 7f8b392
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 37 deletions.
95 changes: 61 additions & 34 deletions data/codings.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,52 @@ func decode(data []byte, decoder *encoding.Decoder) (st string, err error) {
return
}

type CustomEncoding struct {
encDec EncDec
coding byte
}

func NewCustomEncoding(coding byte, encDec EncDec) Encoding {
return &CustomEncoding{
coding: coding,
encDec: encDec,
}
}

// Encode string.
func (c *CustomEncoding) Encode(str string) ([]byte, error) {
return c.encDec.Encode(str)
}

// Decode data to string.
func (c *CustomEncoding) Decode(data []byte) (string, error) {
return c.encDec.Decode(data)
}

// DataCoding flag.
func (c *CustomEncoding) DataCoding() byte {
return c.coding
}

type gsm7bit struct {
packed bool
}

func (c gsm7bit) Encode(str string) ([]byte, error) {
func (c *gsm7bit) Encode(str string) ([]byte, error) {
return encode(str, GSM7(c.packed).NewEncoder())
}

func (c gsm7bit) Decode(data []byte) (string, error) {
func (c *gsm7bit) Decode(data []byte) (string, error) {
return decode(data, GSM7(c.packed).NewDecoder())
}

func (c gsm7bit) DataCoding() byte { return GSM7BITCoding }
func (c *gsm7bit) DataCoding() byte { return GSM7BITCoding }

func (c gsm7bit) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {
func (c *gsm7bit) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {
return uint(len(text)) > octetLimit
}

func (c gsm7bit) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {
func (c *gsm7bit) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {
if octetLimit < 64 {
octetLimit = 134
}
Expand All @@ -93,93 +120,93 @@ func (c gsm7bit) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err

type ascii struct{}

func (c ascii) Encode(str string) ([]byte, error) {
func (*ascii) Encode(str string) ([]byte, error) {
return []byte(str), nil
}

func (c ascii) Decode(data []byte) (string, error) {
func (*ascii) Decode(data []byte) (string, error) {
return string(data), nil
}

func (c ascii) DataCoding() byte { return ASCIICoding }
func (*ascii) DataCoding() byte { return ASCIICoding }

type iso88591 struct{}

func (c iso88591) Encode(str string) ([]byte, error) {
func (*iso88591) Encode(str string) ([]byte, error) {
return encode(str, charmap.ISO8859_1.NewEncoder())
}

func (c iso88591) Decode(data []byte) (string, error) {
func (*iso88591) Decode(data []byte) (string, error) {
return decode(data, charmap.ISO8859_1.NewDecoder())
}

func (c iso88591) DataCoding() byte { return LATIN1Coding }
func (*iso88591) DataCoding() byte { return LATIN1Coding }

type binary8bit1 struct{}

func (c binary8bit1) Encode(_ string) ([]byte, error) {
func (*binary8bit1) Encode(_ string) ([]byte, error) {
return []byte{}, ErrNotImplEncode
}

func (c binary8bit1) Decode(_ []byte) (string, error) {
func (*binary8bit1) Decode(_ []byte) (string, error) {
return "", ErrNotImplDecode
}

func (c binary8bit1) DataCoding() byte { return BINARY8BIT1Coding }
func (*binary8bit1) DataCoding() byte { return BINARY8BIT1Coding }

type binary8bit2 struct{}

func (c binary8bit2) Encode(_ string) ([]byte, error) {
func (*binary8bit2) Encode(_ string) ([]byte, error) {
return []byte{}, ErrNotImplEncode
}

func (c binary8bit2) Decode(_ []byte) (string, error) {
func (*binary8bit2) Decode(_ []byte) (string, error) {
return "", ErrNotImplDecode
}

func (c binary8bit2) DataCoding() byte { return BINARY8BIT2Coding }
func (*binary8bit2) DataCoding() byte { return BINARY8BIT2Coding }

type iso88595 struct{}

func (c iso88595) Encode(str string) ([]byte, error) {
func (*iso88595) Encode(str string) ([]byte, error) {
return encode(str, charmap.ISO8859_5.NewEncoder())
}

func (c iso88595) Decode(data []byte) (string, error) {
func (*iso88595) Decode(data []byte) (string, error) {
return decode(data, charmap.ISO8859_5.NewDecoder())
}

func (c iso88595) DataCoding() byte { return CYRILLICCoding }
func (*iso88595) DataCoding() byte { return CYRILLICCoding }

type iso88598 struct{}

func (c iso88598) Encode(str string) ([]byte, error) {
func (*iso88598) Encode(str string) ([]byte, error) {
return encode(str, charmap.ISO8859_8.NewEncoder())
}

func (c iso88598) Decode(data []byte) (string, error) {
func (*iso88598) Decode(data []byte) (string, error) {
return decode(data, charmap.ISO8859_8.NewDecoder())
}

func (c iso88598) DataCoding() byte { return HEBREWCoding }
func (*iso88598) DataCoding() byte { return HEBREWCoding }

type ucs2 struct{}

func (c ucs2) Encode(str string) ([]byte, error) {
func (*ucs2) Encode(str string) ([]byte, error) {
tmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
return encode(str, tmp.NewEncoder())
}

func (c ucs2) Decode(data []byte) (string, error) {
func (*ucs2) Decode(data []byte) (string, error) {
tmp := unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM)
return decode(data, tmp.NewDecoder())
}

func (c ucs2) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {
func (*ucs2) ShouldSplit(text string, octetLimit uint) (shouldSplit bool) {
return uint(len(text)*2) > octetLimit
}

func (c ucs2) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {
func (c *ucs2) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err error) {
if octetLimit < 64 {
octetLimit = 134
}
Expand Down Expand Up @@ -207,7 +234,7 @@ func (c ucs2) EncodeSplit(text string, octetLimit uint) (allSeg [][]byte, err er
return
}

func (c ucs2) DataCoding() byte { return UCS2Coding }
func (*ucs2) DataCoding() byte { return UCS2Coding }

var (
// GSM7BIT is gsm-7bit encoding.
Expand Down Expand Up @@ -241,14 +268,14 @@ var (
)

var codingMap = map[byte]Encoding{
GSM7BITCoding: GSM7BIT,
ASCIICoding: ASCII,
GSM7BITCoding: GSM7BIT,
ASCIICoding: ASCII,
BINARY8BIT1Coding: BINARY8BIT1,
LATIN1Coding: LATIN1,
LATIN1Coding: LATIN1,
BINARY8BIT2Coding: BINARY8BIT2,
CYRILLICCoding: CYRILLIC,
HEBREWCoding: HEBREW,
UCS2Coding: UCS2,
CYRILLICCoding: CYRILLIC,
HEBREWCoding: HEBREW,
UCS2Coding: UCS2,
}

// FromDataCoding returns encoding from DataCoding value.
Expand Down
23 changes: 23 additions & 0 deletions data/codings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,26 @@ func TestOtherCodings(t *testing.T) {
testEncoding(t, UTF16BE, "ngưỡng cứa cuỗc đợi", "006e006701b01ee1006e0067002000631ee900610020006300751ed70063002001111ee30069")
testEncoding(t, UTF16LE, "ngưỡng cứa cuỗc đợi", "6e006700b001e11e6e00670020006300e91e6100200063007500d71e630020001101e31e6900")
}

type noOpEncDec struct{}

func (*noOpEncDec) Encode(str string) ([]byte, error) {
return []byte(str), nil
}

func (*noOpEncDec) Decode(data []byte) (string, error) {
return string(data), nil
}

func TestCustomEncoding(t *testing.T) {
enc := NewCustomEncoding(GSM7BITCoding, &noOpEncDec{})
require.EqualValues(t, GSM7BITCoding, enc.DataCoding())

encoded, err := enc.Encode("abc")
require.NoError(t, err)
require.Equal(t, []byte("abc"), encoded)

decoded, err := enc.Decode(encoded)
require.NoError(t, err)
require.Equal(t, "abc", decoded)
}
11 changes: 8 additions & 3 deletions pdu/ShortMessage.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type ShortMessage struct {
enc data.Encoding
udHeader UDH
messageData []byte
withoutDataCoding bool
withoutDataCoding bool // purpose of ReplaceSM usage
}

// NewShortMessage returns new ShortMessage.
Expand Down Expand Up @@ -178,8 +178,13 @@ func (c *ShortMessage) Split() (multiSM []*ShortMessage, err error) {
return nil, err
}

ref := getRefNum() // all segments will have the same ref id
multiSM = []*ShortMessage{}
// prealloc result
multiSM = make([]*ShortMessage, 0, len(segments))

// all segments will have the same ref id
ref := getRefNum()

// construct SM(s)
for i, seg := range segments {
// create new SM, encode data
multiSM = append(multiSM, &ShortMessage{
Expand Down
33 changes: 33 additions & 0 deletions pdu/ShortMessage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,45 @@ import (
"github.com/stretchr/testify/require"
)

type customEncoder struct{}

func (*customEncoder) Encode(str string) ([]byte, error) {
return []byte(str), nil
}

func (*customEncoder) Decode(data []byte) (string, error) {
return string(data), nil
}

func TestShortMessage(t *testing.T) {
t.Run("invalidCoding", func(t *testing.T) {
var s ShortMessage
require.NotNil(t, s.SetMessageWithEncoding("agjwklgjkwPфngưỡng", data.LATIN1))
})

t.Run("customCoding", func(t *testing.T) {
var s ShortMessage

customCoding := data.NewCustomEncoding(246, &customEncoder{})
err := s.SetMessageDataWithEncoding([]byte{0x61, 0x62, 0x63}, customCoding) // "abc"
require.NoError(t, err)
require.EqualValues(t, 246, s.Encoding().DataCoding())

m, err := s.GetMessage()
require.Nil(t, err)
require.Equal(t, "abc", m)

// try to get message string with other encoding
m, err = s.GetMessageWithEncoding(data.FromDataCoding(data.UCS2Coding))
require.Nil(t, err)
require.NotEqual(t, "abc", m)

// get message string with custom encoding
m, err = s.GetMessageWithEncoding(customCoding)
require.Nil(t, err)
require.Equal(t, "abc", m)
})

t.Run("invalidSize", func(t *testing.T) {
var s ShortMessage
require.Equal(t, errors.ErrShortMessageLengthTooLarge,
Expand Down

0 comments on commit 7f8b392

Please sign in to comment.