summaryrefslogtreecommitdiff
path: root/vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go')
-rw-r--r--vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go347
1 files changed, 347 insertions, 0 deletions
diff --git a/vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go b/vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go
new file mode 100644
index 000000000..ea79e3a00
--- /dev/null
+++ b/vendor/sigs.k8s.io/structured-merge-diff/v4/value/value.go
@@ -0,0 +1,347 @@
+/*
+Copyright 2018 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package value
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+
+ jsoniter "github.com/json-iterator/go"
+ "gopkg.in/yaml.v2"
+)
+
+var (
+ readPool = jsoniter.NewIterator(jsoniter.ConfigCompatibleWithStandardLibrary).Pool()
+ writePool = jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, nil, 1024).Pool()
+)
+
+// A Value corresponds to an 'atom' in the schema. It should return true
+// for at least one of the IsXXX methods below, or the value is
+// considered "invalid"
+type Value interface {
+ // IsMap returns true if the Value is a Map, false otherwise.
+ IsMap() bool
+ // IsList returns true if the Value is a List, false otherwise.
+ IsList() bool
+ // IsBool returns true if the Value is a bool, false otherwise.
+ IsBool() bool
+ // IsInt returns true if the Value is a int64, false otherwise.
+ IsInt() bool
+ // IsFloat returns true if the Value is a float64, false
+ // otherwise.
+ IsFloat() bool
+ // IsString returns true if the Value is a string, false
+ // otherwise.
+ IsString() bool
+ // IsMap returns true if the Value is null, false otherwise.
+ IsNull() bool
+
+ // AsMap converts the Value into a Map (or panic if the type
+ // doesn't allow it).
+ AsMap() Map
+ // AsMapUsing uses the provided allocator and converts the Value
+ // into a Map (or panic if the type doesn't allow it).
+ AsMapUsing(Allocator) Map
+ // AsList converts the Value into a List (or panic if the type
+ // doesn't allow it).
+ AsList() List
+ // AsListUsing uses the provided allocator and converts the Value
+ // into a List (or panic if the type doesn't allow it).
+ AsListUsing(Allocator) List
+ // AsBool converts the Value into a bool (or panic if the type
+ // doesn't allow it).
+ AsBool() bool
+ // AsInt converts the Value into an int64 (or panic if the type
+ // doesn't allow it).
+ AsInt() int64
+ // AsFloat converts the Value into a float64 (or panic if the type
+ // doesn't allow it).
+ AsFloat() float64
+ // AsString converts the Value into a string (or panic if the type
+ // doesn't allow it).
+ AsString() string
+
+ // Unstructured converts the Value into an Unstructured interface{}.
+ Unstructured() interface{}
+}
+
+// FromJSON is a helper function for reading a JSON document.
+func FromJSON(input []byte) (Value, error) {
+ return FromJSONFast(input)
+}
+
+// FromJSONFast is a helper function for reading a JSON document.
+func FromJSONFast(input []byte) (Value, error) {
+ iter := readPool.BorrowIterator(input)
+ defer readPool.ReturnIterator(iter)
+ return ReadJSONIter(iter)
+}
+
+// ToJSON is a helper function for producing a JSon document.
+func ToJSON(v Value) ([]byte, error) {
+ buf := bytes.Buffer{}
+ stream := writePool.BorrowStream(&buf)
+ defer writePool.ReturnStream(stream)
+ WriteJSONStream(v, stream)
+ b := stream.Buffer()
+ err := stream.Flush()
+ // Help jsoniter manage its buffers--without this, the next
+ // use of the stream is likely to require an allocation. Look
+ // at the jsoniter stream code to understand why. They were probably
+ // optimizing for folks using the buffer directly.
+ stream.SetBuffer(b[:0])
+ return buf.Bytes(), err
+}
+
+// ReadJSONIter reads a Value from a JSON iterator.
+func ReadJSONIter(iter *jsoniter.Iterator) (Value, error) {
+ v := iter.Read()
+ if iter.Error != nil && iter.Error != io.EOF {
+ return nil, iter.Error
+ }
+ return NewValueInterface(v), nil
+}
+
+// WriteJSONStream writes a value into a JSON stream.
+func WriteJSONStream(v Value, stream *jsoniter.Stream) {
+ stream.WriteVal(v.Unstructured())
+}
+
+// ToYAML marshals a value as YAML.
+func ToYAML(v Value) ([]byte, error) {
+ return yaml.Marshal(v.Unstructured())
+}
+
+// Equals returns true iff the two values are equal.
+func Equals(lhs, rhs Value) bool {
+ return EqualsUsing(HeapAllocator, lhs, rhs)
+}
+
+// EqualsUsing uses the provided allocator and returns true iff the two values are equal.
+func EqualsUsing(a Allocator, lhs, rhs Value) bool {
+ if lhs.IsFloat() || rhs.IsFloat() {
+ var lf float64
+ if lhs.IsFloat() {
+ lf = lhs.AsFloat()
+ } else if lhs.IsInt() {
+ lf = float64(lhs.AsInt())
+ } else {
+ return false
+ }
+ var rf float64
+ if rhs.IsFloat() {
+ rf = rhs.AsFloat()
+ } else if rhs.IsInt() {
+ rf = float64(rhs.AsInt())
+ } else {
+ return false
+ }
+ return lf == rf
+ }
+ if lhs.IsInt() {
+ if rhs.IsInt() {
+ return lhs.AsInt() == rhs.AsInt()
+ }
+ return false
+ } else if rhs.IsInt() {
+ return false
+ }
+ if lhs.IsString() {
+ if rhs.IsString() {
+ return lhs.AsString() == rhs.AsString()
+ }
+ return false
+ } else if rhs.IsString() {
+ return false
+ }
+ if lhs.IsBool() {
+ if rhs.IsBool() {
+ return lhs.AsBool() == rhs.AsBool()
+ }
+ return false
+ } else if rhs.IsBool() {
+ return false
+ }
+ if lhs.IsList() {
+ if rhs.IsList() {
+ lhsList := lhs.AsListUsing(a)
+ defer a.Free(lhsList)
+ rhsList := rhs.AsListUsing(a)
+ defer a.Free(rhsList)
+ return lhsList.EqualsUsing(a, rhsList)
+ }
+ return false
+ } else if rhs.IsList() {
+ return false
+ }
+ if lhs.IsMap() {
+ if rhs.IsMap() {
+ lhsList := lhs.AsMapUsing(a)
+ defer a.Free(lhsList)
+ rhsList := rhs.AsMapUsing(a)
+ defer a.Free(rhsList)
+ return lhsList.EqualsUsing(a, rhsList)
+ }
+ return false
+ } else if rhs.IsMap() {
+ return false
+ }
+ if lhs.IsNull() {
+ if rhs.IsNull() {
+ return true
+ }
+ return false
+ } else if rhs.IsNull() {
+ return false
+ }
+ // No field is set, on either objects.
+ return true
+}
+
+// ToString returns a human-readable representation of the value.
+func ToString(v Value) string {
+ if v.IsNull() {
+ return "null"
+ }
+ switch {
+ case v.IsFloat():
+ return fmt.Sprintf("%v", v.AsFloat())
+ case v.IsInt():
+ return fmt.Sprintf("%v", v.AsInt())
+ case v.IsString():
+ return fmt.Sprintf("%q", v.AsString())
+ case v.IsBool():
+ return fmt.Sprintf("%v", v.AsBool())
+ case v.IsList():
+ strs := []string{}
+ list := v.AsList()
+ for i := 0; i < list.Length(); i++ {
+ strs = append(strs, ToString(list.At(i)))
+ }
+ return "[" + strings.Join(strs, ",") + "]"
+ case v.IsMap():
+ strs := []string{}
+ v.AsMap().Iterate(func(k string, v Value) bool {
+ strs = append(strs, fmt.Sprintf("%v=%v", k, ToString(v)))
+ return true
+ })
+ return strings.Join(strs, "")
+ }
+ // No field is set, on either objects.
+ return "{{undefined}}"
+}
+
+// Less provides a total ordering for Value (so that they can be sorted, even
+// if they are of different types).
+func Less(lhs, rhs Value) bool {
+ return Compare(lhs, rhs) == -1
+}
+
+// Compare provides a total ordering for Value (so that they can be
+// sorted, even if they are of different types). The result will be 0 if
+// v==rhs, -1 if v < rhs, and +1 if v > rhs.
+func Compare(lhs, rhs Value) int {
+ return CompareUsing(HeapAllocator, lhs, rhs)
+}
+
+// CompareUsing uses the provided allocator and provides a total
+// ordering for Value (so that they can be sorted, even if they
+// are of different types). The result will be 0 if v==rhs, -1
+// if v < rhs, and +1 if v > rhs.
+func CompareUsing(a Allocator, lhs, rhs Value) int {
+ if lhs.IsFloat() {
+ if !rhs.IsFloat() {
+ // Extra: compare floats and ints numerically.
+ if rhs.IsInt() {
+ return FloatCompare(lhs.AsFloat(), float64(rhs.AsInt()))
+ }
+ return -1
+ }
+ return FloatCompare(lhs.AsFloat(), rhs.AsFloat())
+ } else if rhs.IsFloat() {
+ // Extra: compare floats and ints numerically.
+ if lhs.IsInt() {
+ return FloatCompare(float64(lhs.AsInt()), rhs.AsFloat())
+ }
+ return 1
+ }
+
+ if lhs.IsInt() {
+ if !rhs.IsInt() {
+ return -1
+ }
+ return IntCompare(lhs.AsInt(), rhs.AsInt())
+ } else if rhs.IsInt() {
+ return 1
+ }
+
+ if lhs.IsString() {
+ if !rhs.IsString() {
+ return -1
+ }
+ return strings.Compare(lhs.AsString(), rhs.AsString())
+ } else if rhs.IsString() {
+ return 1
+ }
+
+ if lhs.IsBool() {
+ if !rhs.IsBool() {
+ return -1
+ }
+ return BoolCompare(lhs.AsBool(), rhs.AsBool())
+ } else if rhs.IsBool() {
+ return 1
+ }
+
+ if lhs.IsList() {
+ if !rhs.IsList() {
+ return -1
+ }
+ lhsList := lhs.AsListUsing(a)
+ defer a.Free(lhsList)
+ rhsList := rhs.AsListUsing(a)
+ defer a.Free(rhsList)
+ return ListCompareUsing(a, lhsList, rhsList)
+ } else if rhs.IsList() {
+ return 1
+ }
+ if lhs.IsMap() {
+ if !rhs.IsMap() {
+ return -1
+ }
+ lhsMap := lhs.AsMapUsing(a)
+ defer a.Free(lhsMap)
+ rhsMap := rhs.AsMapUsing(a)
+ defer a.Free(rhsMap)
+ return MapCompareUsing(a, lhsMap, rhsMap)
+ } else if rhs.IsMap() {
+ return 1
+ }
+ if lhs.IsNull() {
+ if !rhs.IsNull() {
+ return -1
+ }
+ return 0
+ } else if rhs.IsNull() {
+ return 1
+ }
+
+ // Invalid Value-- nothing is set.
+ return 0
+}