From acb6966dc11b8c225868458c5bae61f7fa40c0b6 Mon Sep 17 00:00:00 2001 From: Marcos Felipe Date: Tue, 11 Apr 2023 00:02:08 +0100 Subject: [PATCH] Add function to convert to byte array --- cast.go | 11 ++++++- cast_test.go | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++ caste.go | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 1 deletion(-) diff --git a/cast.go b/cast.go index 0cfe941..8ef9ada 100644 --- a/cast.go +++ b/cast.go @@ -6,7 +6,10 @@ // Package cast provides easy and safe casting in Go. package cast -import "time" +import ( + "encoding/binary" + "time" +) // ToBool casts an interface to a bool type. func ToBool(i interface{}) bool { @@ -169,6 +172,12 @@ func ToIntSlice(i interface{}) []int { return v } +// ToBytes casts an interface to a []byte type. +func ToBytes(i interface{}, b binary.ByteOrder) []byte { + v, _ := ToBytesE(i, b) + return v +} + // ToDurationSlice casts an interface to a []time.Duration type. func ToDurationSlice(i interface{}) []time.Duration { v, _ := ToDurationSliceE(i) diff --git a/cast_test.go b/cast_test.go index 016f8c2..7cbee60 100644 --- a/cast_test.go +++ b/cast_test.go @@ -6,10 +6,12 @@ package cast import ( + "encoding/binary" "encoding/json" "errors" "fmt" "html/template" + "math" "path" "reflect" "testing" @@ -289,6 +291,93 @@ func TestToStringE(t *testing.T) { } } +func TestToBytesE(t *testing.T) { + c := qt.New(t) + + //Overkill? + expected8 := []byte{math.MaxInt8} + expectedLittle16 := append([]byte{math.MaxUint8}, math.MaxInt8) + expectedBig16 := append(expected8, math.MaxUint8) + expectedLittle32 := append([]byte{math.MaxUint8, math.MaxUint8}, expectedLittle16...) + expectedBig32 := append(expectedBig16, math.MaxUint8, math.MaxUint8) + expectedLittle64 := append([]byte{math.MaxUint8, math.MaxUint8, math.MaxUint8, math.MaxUint8}, expectedLittle32...) + expectedBig64 := append(expectedBig32, math.MaxUint8, math.MaxUint8, math.MaxUint8, math.MaxUint8) + + expectedU8 := []byte{math.MaxUint8} + expectedLittleU16 := append(expectedU8, expectedU8...) + expectedLittleU32 := append(expectedLittleU16, expectedLittleU16...) + expectedLittleU64 := append(expectedLittleU32, expectedLittleU32...) + expectedBigU16 := append(expected8, expectedU8...) + expectedBigU32 := append(expectedBigU16, expectedLittleU16...) + expectedBigU64 := append(expectedBigU32, expectedLittleU32...) + + tests := []struct { + input interface{} + byteOrder binary.ByteOrder + expect []byte + iserr bool + }{ + //LittleEndian + {byte(math.MaxUint8), binary.LittleEndian, expectedU8, false}, + {math.MaxInt32, binary.LittleEndian, expectedLittle32, false}, + {math.MaxInt64, binary.LittleEndian, expectedLittle64, false}, + {uint(math.MaxUint32), binary.LittleEndian, expectedLittleU32, false}, + {uint(math.MaxUint64), binary.LittleEndian, expectedLittleU64, false}, + {int8(math.MaxInt8), binary.LittleEndian, expected8, false}, + {int16(math.MaxInt16), binary.LittleEndian, expectedLittle16, false}, + {int32(math.MaxInt32), binary.LittleEndian, expectedLittle32, false}, + {int64(math.MaxInt64), binary.LittleEndian, expectedLittle64, false}, + {uint8(math.MaxUint8), binary.LittleEndian, expectedU8, false}, + {uint16(math.MaxUint16), binary.LittleEndian, expectedLittleU16, false}, + {uint32(math.MaxUint32), binary.LittleEndian, expectedLittleU32, false}, + {uint64(math.MaxUint64), binary.LittleEndian, expectedLittleU64, false}, + {[]byte("one time"), binary.LittleEndian, []byte("one time"), false}, + {"one more time", binary.LittleEndian, []byte("one more time"), false}, + //BigEndian + {byte(math.MaxUint8), binary.BigEndian, expectedU8, false}, + {math.MaxInt32, binary.BigEndian, expectedBig32, false}, + {math.MaxInt64, binary.BigEndian, expectedBig64, false}, + {uint(math.MaxInt32), binary.BigEndian, expectedBigU32, false}, + {uint(math.MaxInt64), binary.BigEndian, expectedBigU64, false}, + {int8(math.MaxInt8), binary.BigEndian, expected8, false}, + {int16(math.MaxInt16), binary.BigEndian, expectedBig16, false}, + {int32(math.MaxInt32), binary.BigEndian, expectedBig32, false}, + {int64(math.MaxInt64), binary.BigEndian, expectedBig64, false}, + {uint8(math.MaxUint8), binary.BigEndian, expectedU8, false}, + {uint16(math.MaxInt16), binary.BigEndian, expectedBigU16, false}, + {uint32(math.MaxInt32), binary.BigEndian, expectedBigU32, false}, + {uint64(math.MaxInt64), binary.BigEndian, expectedBigU64, false}, + {[]byte("one time"), binary.BigEndian, []byte("one time"), false}, + {"one more time", binary.BigEndian, []byte("one more time"), false}, + // errors + {testing.T{}, binary.LittleEndian, []byte{}, true}, + {int8(-8), binary.LittleEndian, []byte{}, true}, + {int16(-136), binary.LittleEndian, []byte{}, true}, + {int32(-2184), binary.LittleEndian, []byte{}, true}, + {int64(-34952), binary.LittleEndian, []byte{}, true}, + {true, binary.LittleEndian, []byte{}, true}, + {false, binary.LittleEndian, []byte{}, true}, + {nil, binary.LittleEndian, []byte{}, true}, + } + + for i, test := range tests { + errmsg := qt.Commentf("i = %d", i) // assert helper message + + v, err := ToBytesE(test.input, test.byteOrder) + if test.iserr { + c.Assert(err, qt.IsNotNil, errmsg) + continue + } + + c.Assert(err, qt.IsNil, errmsg) + c.Assert(v, qt.DeepEquals, test.expect, errmsg) + + // Non-E test + v = ToBytes(test.input, test.byteOrder) + c.Assert(v, qt.DeepEquals, test.expect, errmsg) + } +} + type foo struct { val string } diff --git a/caste.go b/caste.go index bccf27a..80c0570 100644 --- a/caste.go +++ b/caste.go @@ -6,10 +6,12 @@ package cast import ( + "encoding/binary" "encoding/json" "errors" "fmt" "html/template" + "math" "reflect" "strconv" "strings" @@ -1340,6 +1342,92 @@ func ToIntSliceE(i interface{}) ([]int, error) { } } +func ToBytesE(i interface{}, b binary.ByteOrder) ([]byte, error) { + i = indirect(i) + + if i == nil { + return []byte{}, fmt.Errorf("unable to cast %#v of type %T to []byte", i, i) + } + + switch v := i.(type) { + case []byte: + return v, nil + case byte: + return []byte{v}, nil + case int8: + if v < 0 { + return []byte{}, errNegativeNotAllowed + } + return []byte{byte(v)}, nil + case int16: + if v < 0 { + return []byte{}, errNegativeNotAllowed + } + a := make([]byte, 2) + b.PutUint16(a, uint16(v)) + return a, nil + case int32: + if v < 0 { + return []byte{}, errNegativeNotAllowed + } + a := make([]byte, 4) + b.PutUint32(a, uint32(v)) + return a, nil + case int64: + if v < 0 { + return []byte{}, errNegativeNotAllowed + } + a := make([]byte, 8) + b.PutUint64(a, uint64(v)) + return a, nil + case uint16: + a := make([]byte, 2) + b.PutUint16(a, v) + return a, nil + case uint32: + a := make([]byte, 4) + b.PutUint32(a, v) + return a, nil + case uint64: + a := make([]byte, 8) + b.PutUint64(a, v) + return a, nil + case int: + switch true { + case v < 0: + return []byte{}, errNegativeNotAllowed + case v <= math.MaxUint32: + a := make([]byte, 4) + b.PutUint32(a, uint32(v)) + return a, nil + default: + a := make([]byte, 8) + b.PutUint64(a, uint64(v)) + return a, nil + } + case uint: + switch true { + case v < 0: + return []byte{}, errNegativeNotAllowed + case v <= math.MaxUint32: + a := make([]byte, 4) + b.PutUint32(a, uint32(v)) + return a, nil + case v <= math.MaxUint64: + a := make([]byte, 8) + b.PutUint64(a, uint64(v)) + return a, nil + default: + return []byte{}, fmt.Errorf("unable to cast %#v of type %T to []byte", i, i) + } + case string: + return []byte(v), nil + default: + return []byte{}, fmt.Errorf("unable to cast %#v of type %T to []byte", i, i) + } + +} + // ToDurationSliceE casts an interface to a []time.Duration type. func ToDurationSliceE(i interface{}) ([]time.Duration, error) { if i == nil {