diff --git a/etf/decode.go b/etf/decode.go index bf245b0f..0c70762a 100644 --- a/etf/decode.go +++ b/etf/decode.go @@ -146,7 +146,7 @@ func Decode(packet []byte, cache []Atom) (retTerm Term, retByte []byte, retErr e return nil, nil, errMalformedString } - term = string(packet[2 : n+2]) + term = String(packet[2 : n+2]) packet = packet[n+2:] case ettCacheRef: @@ -357,10 +357,7 @@ func Decode(packet []byte, cache []Atom) (retTerm Term, retByte []byte, retErr e return nil, nil, errMalformedBinary } - b := make([]byte, n) - copy(b, packet[4:n+4]) - - term = b + term = string(packet[4 : n+4]) packet = packet[n+4:] case ettNil: @@ -438,7 +435,10 @@ func Decode(packet []byte, cache []Atom) (retTerm Term, retByte []byte, retErr e b := make([]byte, n) copy(b, packet[5:n+5]) - b[n-1] = b[n-1] >> (8 - bits) + + if bits != 8 { + b[n-1] = b[n-1] >> (8 - bits) + } term = b packet = packet[n+5:] diff --git a/etf/decode_test.go b/etf/decode_test.go index 497b1fb8..1d330bae 100644 --- a/etf/decode_test.go +++ b/etf/decode_test.go @@ -35,7 +35,7 @@ func TestDecodeAtom(t *testing.T) { } func TestDecodeString(t *testing.T) { - expected := "abc" + expected := String("abc") packet := []byte{ettString, 0, 3, 97, 98, 99} term, _, err := Decode(packet, []Atom{}) if err != nil || term != expected { @@ -198,7 +198,7 @@ func TestDecodeMap(t *testing.T) { Atom("abc"): 123, "abc": 4.56, } - packet := []byte{116, 0, 0, 0, 2, 100, 0, 3, 97, 98, 99, 97, 123, 107, 0, 3, 97, 98, + packet := []byte{116, 0, 0, 0, 2, 100, 0, 3, 97, 98, 99, 97, 123, 109, 0, 0, 0, 3, 97, 98, 99, 70, 64, 18, 61, 112, 163, 215, 10, 61} term, _, err := Decode(packet, []Atom{}) @@ -214,8 +214,23 @@ func TestDecodeMap(t *testing.T) { } func TestDecodeBinary(t *testing.T) { + expected := "abc" + packet := []byte{ettBinary, 0, 0, 0, 3, 97, 98, 99} + term, _, err := Decode(packet, []Atom{}) + if err != nil || term != expected { + t.Fatal(err) + } + + packet = []byte{ettBinary, 0, 3, 97, 98, 99} + term, _, err = Decode(packet, []Atom{}) + if err != errMalformedBinary { + t.Fatal(err) + } +} + +func TestDecodeBitBinary(t *testing.T) { expected := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} - packet := []byte{ettBinary, 0, 0, 0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0} + packet := []byte{ettBitBinary, 0, 0, 0, 10, 8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0} term, _, err := Decode(packet, []Atom{}) if err != nil { @@ -228,9 +243,9 @@ func TestDecodeBinary(t *testing.T) { } } -func TestDecodeBitBinary(t *testing.T) { +func TestDecodeBitBinaryWithLastBits(t *testing.T) { expected := []byte{1, 2, 3, 4, 5} - packet := []byte{77, 0, 0, 0, 5, 3, 1, 2, 3, 4, 160} + packet := []byte{ettBitBinary, 0, 0, 0, 5, 3, 1, 2, 3, 4, 160} term, _, err := Decode(packet, []Atom{}) if err != nil { @@ -401,9 +416,9 @@ func TestDecodeComplex(t *testing.T) { expected := Tuple{"hello", List{}, Map{Atom("v1"): List{Tuple{3, 13, 3.13}, Tuple{Atom("abc"), "abc"}}, Atom("v2"): int64(12345)}} - packet := []byte{104, 3, 107, 0, 5, 104, 101, 108, 108, 111, 106, 116, 0, 0, 0, 2, + packet := []byte{104, 3, 109, 0, 0, 0, 5, 104, 101, 108, 108, 111, 106, 116, 0, 0, 0, 2, 100, 0, 2, 118, 49, 108, 0, 0, 0, 2, 104, 3, 97, 3, 97, 13, 70, 64, 9, 10, - 61, 112, 163, 215, 10, 104, 2, 100, 0, 3, 97, 98, 99, 107, 0, 3, 97, 98, + 61, 112, 163, 215, 10, 104, 2, 100, 0, 3, 97, 98, 99, 109, 0, 0, 0, 3, 97, 98, 99, 106, 100, 0, 2, 118, 50, 98, 0, 0, 48, 57} term, _, err := Decode(packet, []Atom{}) if err != nil { diff --git a/etf/encode.go b/etf/encode.go index 57681d84..daf031ff 100644 --- a/etf/encode.go +++ b/etf/encode.go @@ -403,15 +403,15 @@ func Encode(term Term, b *lib.Buffer, } lenString := len(t) - if lenString > 65535 { + if lenString > math.MaxUint32 { return ErrStringTooLong } - // 1 (ettString) + 2 (len) + string - buf := b.Extend(1 + 2 + lenString) - buf[0] = ettString - binary.BigEndian.PutUint16(buf[1:3], uint16(lenString)) - copy(buf[3:], t) + // 1 (ettBinary) + 4 (len) + string + buf := b.Extend(1 + 4 + lenString) + buf[0] = ettBinary + binary.BigEndian.PutUint32(buf[1:5], uint32(lenString)) + copy(buf[5:], t) case Atom: if cacheEnabled && cacheIndex < 256 { @@ -514,6 +514,38 @@ func Encode(term Term, b *lib.Buffer, stringAsCharlist: stringAsCharlist, } + case String: + // Spec: optimization for sending lists of bytes + // See: https://erlang.org/doc/apps/erts/erl_ext_dist.html#string_ext + lenString := len(t) + + charlist := []rune(t) + lenCharlist := len(charlist) + // each character takes up only one byte in extended ASCII charset (i.e. Latin1) + if lenString != lenCharlist { + // Spec: when unicode detected, must send list of unicode/rune, as list of UTF8 bytes is a "StrangeList" in Erlang + // See: https://erlang.org/doc/apps/stdlib/unicode_usage.html#lists-of-utf-8-bytes + // Usage: erl +pc unicode -name erl-demo@localhost -setcookie 123 + term = charlist + goto recasting + } else if lenString > 65535 { + // Spec: implementations must ensure that lists longer than 65535 elements are encoded as LIST_EXT. + // See: https://erlang.org/doc/apps/erts/erl_ext_dist.html#string_ext + l := make(List, lenString) + for i := 0; i < lenString; i++ { + // each element = one char + l[i] = t[i] + } + term = l + goto recasting + } + + // 1 (ettString) + 2 (len) + string + buf := b.Extend(1 + 2 + lenString) + buf[0] = ettString + binary.BigEndian.PutUint16(buf[1:3], uint16(lenString)) + copy(buf[3:], t) + case List: lenList := len(t) buf := b.Extend(5) @@ -527,12 +559,30 @@ func Encode(term Term, b *lib.Buffer, stringAsCharlist: stringAsCharlist, } + case []rune: + lenList := len(t) + buf := b.Extend(5) + buf[0] = ettList + binary.BigEndian.PutUint32(buf[1:], uint32(lenList)) + l := make(List, lenList) + for i := 0; i < lenList; i++ { + l[i] = t[i] + } + child = &stackElement{ + parent: stack, + termType: ettList, + term: l, + children: lenList + 1, + stringAsCharlist: stringAsCharlist, + } + case []byte: lenBinary := len(t) - buf := b.Extend(1 + 4 + lenBinary) - buf[0] = ettBinary + buf := b.Extend(1 + 4 + 1 + lenBinary) + buf[0] = ettBitBinary binary.BigEndian.PutUint32(buf[1:5], uint32(lenBinary)) - copy(buf[5:], t) + buf[5] = 8 // 1 byte = 8 bits + copy(buf[6:], t) default: v := reflect.ValueOf(t) diff --git a/etf/encode_test.go b/etf/encode_test.go index 5a635177..4e7633e6 100644 --- a/etf/encode_test.go +++ b/etf/encode_test.go @@ -1,10 +1,12 @@ package etf import ( + "bytes" "context" "fmt" "math/big" "reflect" + "strings" "testing" "github.com/halturin/ergo/lib" @@ -196,12 +198,43 @@ func TestEncodeString(t *testing.T) { b := lib.TakeBuffer() defer lib.ReleaseBuffer(b) - expected := []byte{ettString, 0, 52, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 46, 32, 228, 189, 160, 229, 165, 189, 228, 184, 150, 231, 149, 140, 46, 32, 208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130, 32, 208, 188, 208, 184, 209, 128, 46, 32, 240, 159, 154, 128} - err := Encode("Hello World. 你好世界. Привет мир. 🚀", b, nil, nil, nil) + expected := []byte{ettString, 0, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 46} + + err := Encode(String("Hello World."), b, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(b.B, expected) { + fmt.Println("exp", expected) + fmt.Println("got", b.B) + t.Fatal("incorrect value") + } + + b.Reset() + lenString := 65555 + err = Encode(String(strings.Repeat("a", lenString)), b, nil, nil, nil) if err != nil { t.Fatal(err) } + expected = []byte{ettList, 0, 1, 0, 19} + // binary.BigEndian.PutUint32(expected[1:], uint32(lenString)) + expected = append(expected, bytes.Repeat([]byte{97, 97}, lenString)...) + expected = append(expected, ettNil) + if !reflect.DeepEqual(b.B, expected) { + fmt.Println("exp", expected) + fmt.Println("got", b.B) + t.Fatal("incorrect value") + } + + b.Reset() + err = Encode(String("Hello World. 你好世界. Привет мир. 🚀"), b, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + + expected = []byte{ettList, 0, 0, 0, 32, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 46, 97, 32, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 46, 97, 32, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 60, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 46, 97, 32, 98, 0, 1, 246, 128, 106} if !reflect.DeepEqual(b.B, expected) { fmt.Println("exp", expected) fmt.Println("got", b.B) @@ -312,12 +345,29 @@ func TestEncodeBinary(t *testing.T) { b := lib.TakeBuffer() defer lib.ReleaseBuffer(b) + expected := []byte{ettBinary, 0, 0, 0, 52, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 46, 32, 228, 189, 160, 229, 165, 189, 228, 184, 150, 231, 149, 140, 46, 32, 208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130, 32, 208, 188, 208, 184, 209, 128, 46, 32, 240, 159, 154, 128} + err := Encode("Hello World. 你好世界. Привет мир. 🚀", b, nil, nil, nil) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(b.B, expected) { + fmt.Println("exp", expected) + fmt.Println("got", b.B) + t.Fatal("incorrect value") + } +} + +func TestEncodeBitBinary(t *testing.T) { + b := lib.TakeBuffer() + defer lib.ReleaseBuffer(b) + err := Encode([]byte{1, 2, 3, 4, 5}, b, nil, nil, nil) if err != nil { t.Fatal(err) } - expected := []byte{ettBinary, 0, 0, 0, 5, 1, 2, 3, 4, 5} + expected := []byte{ettBitBinary, 0, 0, 0, 5, 8, 1, 2, 3, 4, 5} if !reflect.DeepEqual(b.B, expected) { t.Fatal("incorrect value") } @@ -421,9 +471,9 @@ func TestEncodeMap(t *testing.T) { // map has no guarantee of key order, so the result could be different expected := []byte{116, 0, 0, 0, 2, 119, 4, 107, 101, 121, 49, 98, 0, 0, 48, 57, 119, 4, - 107, 101, 121, 50, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, + 107, 101, 121, 50, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100} - expected1 := []byte{116, 0, 0, 0, 2, 119, 4, 107, 101, 121, 50, 107, 0, 11, 104, 101, + expected1 := []byte{116, 0, 0, 0, 2, 119, 4, 107, 101, 121, 50, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 107, 101, 121, 49, 98, 0, 0, 48, 57} term := Map{ @@ -449,9 +499,9 @@ func TestEncodeGoMap(t *testing.T) { // map has no guarantee of key order, so the result could be different expected := []byte{116, 0, 0, 0, 2, 119, 4, 107, 101, 121, 49, 98, 0, 0, 48, 57, 119, 4, - 107, 101, 121, 50, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, + 107, 101, 121, 50, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100} - expected1 := []byte{116, 0, 0, 0, 2, 119, 4, 107, 101, 121, 50, 107, 0, 11, 104, 101, + expected1 := []byte{116, 0, 0, 0, 2, 119, 4, 107, 101, 121, 50, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 107, 101, 121, 49, 98, 0, 0, 48, 57} term := map[Atom]interface{}{ @@ -475,7 +525,7 @@ func TestEncodeStruct(t *testing.T) { b := lib.TakeBuffer() defer lib.ReleaseBuffer(b) - expected := []byte{116, 0, 0, 0, 3, 119, 15, 83, 116, 114, 117, 99, 116, 84, 111, 77, 97, 112, 75, 101, 121, 49, 98, 0, 0, 48, 57, 119, 15, 83, 116, 114, 117, 99, 116, 84, 111, 77, 97, 112, 75, 101, 121, 50, 107, 0, 22, 112, 111, 105, 110, 116, 101, 114, 32, 116, 111, 32, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 15, 83, 116, 114, 117, 99, 116, 84, 111, 77, 97, 112, 75, 101, 121, 51, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100} + expected := []byte{116, 0, 0, 0, 3, 119, 15, 83, 116, 114, 117, 99, 116, 84, 111, 77, 97, 112, 75, 101, 121, 49, 98, 0, 0, 48, 57, 119, 15, 83, 116, 114, 117, 99, 116, 84, 111, 77, 97, 112, 75, 101, 121, 50, 109, 0, 0, 0, 22, 112, 111, 105, 110, 116, 101, 114, 32, 116, 111, 32, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 15, 83, 116, 114, 117, 99, 116, 84, 111, 77, 97, 112, 75, 101, 121, 51, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100} s := "pointer to hello world" term := struct { @@ -561,7 +611,7 @@ func TestEncodeStructWithNestedPointers(t *testing.T) { Key9: &nested, } - expected := []byte{116, 0, 0, 0, 2, 119, 6, 78, 101, 115, 116, 101, 100, 116, 0, 0, 0, 8, 119, 4, 75, 101, 121, 49, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 50, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 51, 97, 123, 119, 4, 75, 101, 121, 52, 97, 123, 119, 4, 75, 101, 121, 53, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 54, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 55, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 56, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 57, 116, 0, 0, 0, 8, 119, 4, 75, 101, 121, 49, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 50, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 51, 97, 123, 119, 4, 75, 101, 121, 52, 97, 123, 119, 4, 75, 101, 121, 53, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 54, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 55, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 56, 115, 4, 116, 114, 117, 101} + expected := []byte{116, 0, 0, 0, 2, 119, 6, 78, 101, 115, 116, 101, 100, 116, 0, 0, 0, 8, 119, 4, 75, 101, 121, 49, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 50, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 51, 97, 123, 119, 4, 75, 101, 121, 52, 97, 123, 119, 4, 75, 101, 121, 53, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 54, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 55, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 56, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 57, 116, 0, 0, 0, 8, 119, 4, 75, 101, 121, 49, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 50, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 51, 97, 123, 119, 4, 75, 101, 121, 52, 97, 123, 119, 4, 75, 101, 121, 53, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 54, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 55, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 56, 115, 4, 116, 114, 117, 101} err := Encode(term, b, nil, nil, nil) if err != nil { @@ -578,7 +628,7 @@ func TestEncodeStructWithNestedPointers(t *testing.T) { termWithNil := Tst{ Nested: nested, } - expectedWithNil := []byte{116, 0, 0, 0, 2, 119, 6, 78, 101, 115, 116, 101, 100, 116, 0, 0, 0, 8, 119, 4, 75, 101, 121, 49, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 50, 107, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 51, 97, 123, 119, 4, 75, 101, 121, 52, 97, 123, 119, 4, 75, 101, 121, 53, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 54, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 55, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 56, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 57, 106} + expectedWithNil := []byte{116, 0, 0, 0, 2, 119, 6, 78, 101, 115, 116, 101, 100, 116, 0, 0, 0, 8, 119, 4, 75, 101, 121, 49, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 50, 109, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 119, 4, 75, 101, 121, 51, 97, 123, 119, 4, 75, 101, 121, 52, 97, 123, 119, 4, 75, 101, 121, 53, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 54, 70, 64, 9, 30, 184, 81, 235, 133, 31, 119, 4, 75, 101, 121, 55, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 56, 115, 4, 116, 114, 117, 101, 119, 4, 75, 101, 121, 57, 106} err = Encode(termWithNil, b1, nil, nil, nil) if err != nil { @@ -595,7 +645,7 @@ func TestEncodeStructWithTags(t *testing.T) { b := lib.TakeBuffer() defer lib.ReleaseBuffer(b) - expected := []byte{116, 0, 0, 0, 4, 119, 4, 75, 101, 121, 49, 107, 0, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 119, 17, 99, 117, 115, 116, 111, 109, 95, 102, 105, 101, 108, 100, 95, 110, 97, 109, 101, 108, 0, 0, 0, 3, 108, 0, 0, 0, 7, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 13, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 28, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 14, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 106, 119, 4, 75, 101, 121, 51, 116, 0, 0, 0, 2, 119, 10, 78, 101, 115, 116, 101, 100, 75, 101, 121, 49, 107, 0, 52, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 32, 228, 189, 160, 229, 165, 189, 228, 184, 150, 231, 149, 140, 33, 32, 208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130, 32, 208, 156, 208, 184, 209, 128, 33, 32, 240, 159, 154, 128, 119, 5, 102, 105, 101, 108, 100, 116, 0, 0, 0, 1, 107, 0, 7, 109, 97, 112, 95, 107, 101, 121, 108, 0, 0, 0, 32, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 33, 97, 32, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 33, 97, 32, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 28, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 119, 4, 75, 101, 121, 52, 108, 0, 0, 0, 2, 108, 0, 0, 0, 3, 108, 0, 0, 0, 7, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 13, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 28, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 14, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 106, 108, 0, 0, 0, 3, 108, 0, 0, 0, 7, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 13, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 28, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 14, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 106, 106} + expected := []byte{116, 0, 0, 0, 4, 119, 4, 75, 101, 121, 49, 109, 0, 0, 0, 12, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 119, 17, 99, 117, 115, 116, 111, 109, 95, 102, 105, 101, 108, 100, 95, 110, 97, 109, 101, 108, 0, 0, 0, 3, 108, 0, 0, 0, 7, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 13, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 28, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 14, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 106, 119, 4, 75, 101, 121, 51, 116, 0, 0, 0, 2, 119, 10, 78, 101, 115, 116, 101, 100, 75, 101, 121, 49, 109, 0, 0, 0, 52, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 32, 228, 189, 160, 229, 165, 189, 228, 184, 150, 231, 149, 140, 33, 32, 208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130, 32, 208, 156, 208, 184, 209, 128, 33, 32, 240, 159, 154, 128, 119, 5, 102, 105, 101, 108, 100, 116, 0, 0, 0, 1, 109, 0, 0, 0, 7, 109, 97, 112, 95, 107, 101, 121, 108, 0, 0, 0, 32, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 33, 97, 32, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 33, 97, 32, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 28, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 119, 4, 75, 101, 121, 52, 108, 0, 0, 0, 2, 108, 0, 0, 0, 3, 108, 0, 0, 0, 7, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 13, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 28, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 14, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 106, 108, 0, 0, 0, 3, 108, 0, 0, 0, 7, 98, 0, 0, 79, 96, 98, 0, 0, 89, 125, 98, 0, 0, 78, 22, 98, 0, 0, 117, 76, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 13, 98, 0, 0, 4, 31, 98, 0, 0, 4, 64, 98, 0, 0, 4, 56, 98, 0, 0, 4, 50, 98, 0, 0, 4, 53, 98, 0, 0, 4, 66, 97, 32, 98, 0, 0, 4, 28, 98, 0, 0, 4, 56, 98, 0, 0, 4, 64, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 108, 0, 0, 0, 14, 97, 72, 97, 101, 97, 108, 97, 108, 97, 111, 97, 32, 97, 87, 97, 111, 97, 114, 97, 108, 97, 100, 97, 33, 97, 32, 98, 0, 1, 246, 128, 106, 106, 106} type Nested struct { NestedKey1 string diff --git a/etf/etf.go b/etf/etf.go index e37506a6..d7b8468c 100644 --- a/etf/etf.go +++ b/etf/etf.go @@ -11,6 +11,7 @@ type Term interface{} type Tuple []Term type List []Term type Atom string +type String string // Extended-ASCII string type Map map[Term]Term type Pid struct { @@ -57,10 +58,18 @@ func StringTerm(t Term) (s string, ok bool) { switch x := t.(type) { case Atom: s = string(x) + case String: + s = string(x) case string: s = x case []byte: s = string(x) + case List: + str, err := convertCharlistToString(x) + if err != nil { + ok = false + } + s = str default: ok = false } @@ -207,10 +216,16 @@ func termIntoStruct(term Term, dest reflect.Value, charlistToString bool) error switch v := term.(type) { case Atom: dest.SetString(string(v)) - return nil + case String: + dest.SetString(string(v)) case string: - dest.SetString(v) - return nil + if t.Kind() == reflect.String { + dest.SetString(v) + } else if t == reflect.SliceOf(reflect.TypeOf(byte(1))) { + dest.Set(reflect.ValueOf([]byte(v))) + } else { + return NewInvalidTypesError(t, term) + } case []byte: if t.Kind() == reflect.String { dest.SetString(string(v)) diff --git a/etf/etf_test.go b/etf/etf_test.go index e66f94a5..d42979ff 100644 --- a/etf/etf_test.go +++ b/etf/etf_test.go @@ -90,6 +90,9 @@ func TestTermIntoStruct_Struct(t *testing.T) { A []bool B uint32 C string + D String + E Atom + F []byte } type testStruct struct { @@ -110,6 +113,9 @@ func TestTermIntoStruct_Struct(t *testing.T) { A: []bool{true, false, false, true, false}, B: 8765, C: "test value", + D: String("test"), + E: Atom("value"), + F: []byte{97, 127, 255}, }, BB: 3.13, CC: &testStruct{ @@ -127,6 +133,9 @@ func TestTermIntoStruct_Struct(t *testing.T) { List{true, false, false, true, false}, // A []bool 8765, // B uint32 "test value", // C string + "test", // D String (string -> String) + "value", // E Atom (string -> Atom) + string([]byte{97, 127, 255}), // F []byte (string -> []byte) }, 3.13, // BB float64 Tuple{ // CC *testStruct @@ -247,6 +256,9 @@ func TestTermMapIntoStruct_Struct(t *testing.T) { A []bool `etf:"a"` B uint32 `etf:"b"` C string `etf:"c"` + D String `etf:"d"` + E Atom `etf:"e"` + F []byte `etf:"f"` } dest := testStruct{} @@ -255,12 +267,18 @@ func TestTermMapIntoStruct_Struct(t *testing.T) { A: []bool{false, true, true}, B: 3233, C: "hello world", + D: String("hello"), + E: Atom("world"), + F: []byte{97, 127, 255}, } term := Map{ Atom("a"): List{false, true, true}, "b": 3233, Atom("c"): "hello world", + Atom("d"): String("hello"), + Atom("e"): Atom("world"), + Atom("f"): []byte{97, 127, 255}, } if err := TermMapIntoStruct(term, &dest); err != nil { @@ -278,6 +296,9 @@ func TestTermProplistIntoStruct(t *testing.T) { A []bool `etf:"a"` B uint32 `etf:"b"` C string `etf:"c"` + D String `etf:"d"` + E Atom `etf:"e"` + F []byte `etf:"f"` } dest := testStruct{} @@ -286,11 +307,17 @@ func TestTermProplistIntoStruct(t *testing.T) { A: []bool{false, true, true}, B: 3233, C: "hello world", + D: String("hello"), + E: Atom("world"), + F: []byte{97, 127, 255}, } termList := List{ Tuple{Atom("a"), List{false, true, true}}, Tuple{"b", 3233}, Tuple{Atom("c"), "hello world"}, + Tuple{Atom("d"), String("hello")}, + Tuple{Atom("e"), Atom("world")}, + Tuple{Atom("f"), []byte{97, 127, 255}}, } if err := TermProplistIntoStruct(termList, &dest); err != nil {