aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/zclconf/go-cty/cty/function/stdlib/bytes.go
blob: a132e0cde54c1a1035b094ed50c9b8dd01d94911 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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})
}