blob: a132e0cde54c1a1035b094ed50c9b8dd01d94911 [file] [log] [blame]
package stdlib
import (
"fmt"
"reflect"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
"github.com/zclconf/go-cty/cty/gocty"
)
// Bytes is a capsule type that can be used with the binary functions to
// support applications that need to support raw buffers in addition to
// UTF-8 strings.
var Bytes = cty.Capsule("bytes", reflect.TypeOf([]byte(nil)))
// BytesVal creates a new Bytes value from the given buffer, which must be
// non-nil or this function will panic.
//
// Once a byte slice has been wrapped in a Bytes capsule, its underlying array
// must be considered immutable.
func BytesVal(buf []byte) cty.Value {
if buf == nil {
panic("can't make Bytes value from nil slice")
}
return cty.CapsuleVal(Bytes, &buf)
}
// BytesLen is a Function that returns the length of the buffer encapsulated
// in a Bytes value.
var BytesLenFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "buf",
Type: Bytes,
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(cty.Number),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
bufPtr := args[0].EncapsulatedValue().(*[]byte)
return cty.NumberIntVal(int64(len(*bufPtr))), nil
},
})
// BytesSlice is a Function that returns a slice of the given Bytes value.
var BytesSliceFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "buf",
Type: Bytes,
AllowDynamicType: true,
},
{
Name: "offset",
Type: cty.Number,
AllowDynamicType: true,
},
{
Name: "length",
Type: cty.Number,
AllowDynamicType: true,
},
},
Type: function.StaticReturnType(Bytes),
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
bufPtr := args[0].EncapsulatedValue().(*[]byte)
var offset, length int
var err error
err = gocty.FromCtyValue(args[1], &offset)
if err != nil {
return cty.NilVal, err
}
err = gocty.FromCtyValue(args[2], &length)
if err != nil {
return cty.NilVal, err
}
if offset < 0 || length < 0 {
return cty.NilVal, fmt.Errorf("offset and length must be non-negative")
}
if offset > len(*bufPtr) {
return cty.NilVal, fmt.Errorf(
"offset %d is greater than total buffer length %d",
offset, len(*bufPtr),
)
}
end := offset + length
if end > len(*bufPtr) {
return cty.NilVal, fmt.Errorf(
"offset %d + length %d is greater than total buffer length %d",
offset, length, len(*bufPtr),
)
}
return BytesVal((*bufPtr)[offset:end]), nil
},
})
func BytesLen(buf cty.Value) (cty.Value, error) {
return BytesLenFunc.Call([]cty.Value{buf})
}
func BytesSlice(buf cty.Value, offset cty.Value, length cty.Value) (cty.Value, error) {
return BytesSliceFunc.Call([]cty.Value{buf, offset, length})
}