1package stdlib 2 3import ( 4 "fmt" 5 "reflect" 6 7 "github.com/zclconf/go-cty/cty" 8 "github.com/zclconf/go-cty/cty/function" 9 "github.com/zclconf/go-cty/cty/gocty" 10) 11 12// Bytes is a capsule type that can be used with the binary functions to 13// support applications that need to support raw buffers in addition to 14// UTF-8 strings. 15var Bytes = cty.Capsule("bytes", reflect.TypeOf([]byte(nil))) 16 17// BytesVal creates a new Bytes value from the given buffer, which must be 18// non-nil or this function will panic. 19// 20// Once a byte slice has been wrapped in a Bytes capsule, its underlying array 21// must be considered immutable. 22func BytesVal(buf []byte) cty.Value { 23 if buf == nil { 24 panic("can't make Bytes value from nil slice") 25 } 26 27 return cty.CapsuleVal(Bytes, &buf) 28} 29 30// BytesLen is a Function that returns the length of the buffer encapsulated 31// in a Bytes value. 32var BytesLenFunc = function.New(&function.Spec{ 33 Params: []function.Parameter{ 34 { 35 Name: "buf", 36 Type: Bytes, 37 AllowDynamicType: true, 38 }, 39 }, 40 Type: function.StaticReturnType(cty.Number), 41 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 42 bufPtr := args[0].EncapsulatedValue().(*[]byte) 43 return cty.NumberIntVal(int64(len(*bufPtr))), nil 44 }, 45}) 46 47// BytesSlice is a Function that returns a slice of the given Bytes value. 48var BytesSliceFunc = function.New(&function.Spec{ 49 Params: []function.Parameter{ 50 { 51 Name: "buf", 52 Type: Bytes, 53 AllowDynamicType: true, 54 }, 55 { 56 Name: "offset", 57 Type: cty.Number, 58 AllowDynamicType: true, 59 }, 60 { 61 Name: "length", 62 Type: cty.Number, 63 AllowDynamicType: true, 64 }, 65 }, 66 Type: function.StaticReturnType(Bytes), 67 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 68 bufPtr := args[0].EncapsulatedValue().(*[]byte) 69 70 var offset, length int 71 72 var err error 73 err = gocty.FromCtyValue(args[1], &offset) 74 if err != nil { 75 return cty.NilVal, err 76 } 77 err = gocty.FromCtyValue(args[2], &length) 78 if err != nil { 79 return cty.NilVal, err 80 } 81 82 if offset < 0 || length < 0 { 83 return cty.NilVal, fmt.Errorf("offset and length must be non-negative") 84 } 85 86 if offset > len(*bufPtr) { 87 return cty.NilVal, fmt.Errorf( 88 "offset %d is greater than total buffer length %d", 89 offset, len(*bufPtr), 90 ) 91 } 92 93 end := offset + length 94 95 if end > len(*bufPtr) { 96 return cty.NilVal, fmt.Errorf( 97 "offset %d + length %d is greater than total buffer length %d", 98 offset, length, len(*bufPtr), 99 ) 100 } 101 102 return BytesVal((*bufPtr)[offset:end]), nil 103 }, 104}) 105 106func BytesLen(buf cty.Value) (cty.Value, error) { 107 return BytesLenFunc.Call([]cty.Value{buf}) 108} 109 110func BytesSlice(buf cty.Value, offset cty.Value, length cty.Value) (cty.Value, error) { 111 return BytesSliceFunc.Call([]cty.Value{buf, offset, length}) 112} 113