...

Source file src/sigs.k8s.io/structured-merge-diff/v4/value/value.go

Documentation: sigs.k8s.io/structured-merge-diff/v4/value

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package value
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"strings"
    24  
    25  	jsoniter "github.com/json-iterator/go"
    26  	"gopkg.in/yaml.v2"
    27  )
    28  
    29  var (
    30  	readPool  = jsoniter.NewIterator(jsoniter.ConfigCompatibleWithStandardLibrary).Pool()
    31  	writePool = jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, nil, 1024).Pool()
    32  )
    33  
    34  // A Value corresponds to an 'atom' in the schema. It should return true
    35  // for at least one of the IsXXX methods below, or the value is
    36  // considered "invalid"
    37  type Value interface {
    38  	// IsMap returns true if the Value is a Map, false otherwise.
    39  	IsMap() bool
    40  	// IsList returns true if the Value is a List, false otherwise.
    41  	IsList() bool
    42  	// IsBool returns true if the Value is a bool, false otherwise.
    43  	IsBool() bool
    44  	// IsInt returns true if the Value is a int64, false otherwise.
    45  	IsInt() bool
    46  	// IsFloat returns true if the Value is a float64, false
    47  	// otherwise.
    48  	IsFloat() bool
    49  	// IsString returns true if the Value is a string, false
    50  	// otherwise.
    51  	IsString() bool
    52  	// IsMap returns true if the Value is null, false otherwise.
    53  	IsNull() bool
    54  
    55  	// AsMap converts the Value into a Map (or panic if the type
    56  	// doesn't allow it).
    57  	AsMap() Map
    58  	// AsMapUsing uses the provided allocator and converts the Value
    59  	// into a Map (or panic if the type doesn't allow it).
    60  	AsMapUsing(Allocator) Map
    61  	// AsList converts the Value into a List (or panic if the type
    62  	// doesn't allow it).
    63  	AsList() List
    64  	// AsListUsing uses the provided allocator and converts the Value
    65  	// into a List (or panic if the type doesn't allow it).
    66  	AsListUsing(Allocator) List
    67  	// AsBool converts the Value into a bool (or panic if the type
    68  	// doesn't allow it).
    69  	AsBool() bool
    70  	// AsInt converts the Value into an int64 (or panic if the type
    71  	// doesn't allow it).
    72  	AsInt() int64
    73  	// AsFloat converts the Value into a float64 (or panic if the type
    74  	// doesn't allow it).
    75  	AsFloat() float64
    76  	// AsString converts the Value into a string (or panic if the type
    77  	// doesn't allow it).
    78  	AsString() string
    79  
    80  	// Unstructured converts the Value into an Unstructured interface{}.
    81  	Unstructured() interface{}
    82  }
    83  
    84  // FromJSON is a helper function for reading a JSON document.
    85  func FromJSON(input []byte) (Value, error) {
    86  	return FromJSONFast(input)
    87  }
    88  
    89  // FromJSONFast is a helper function for reading a JSON document.
    90  func FromJSONFast(input []byte) (Value, error) {
    91  	iter := readPool.BorrowIterator(input)
    92  	defer readPool.ReturnIterator(iter)
    93  	return ReadJSONIter(iter)
    94  }
    95  
    96  // ToJSON is a helper function for producing a JSon document.
    97  func ToJSON(v Value) ([]byte, error) {
    98  	buf := bytes.Buffer{}
    99  	stream := writePool.BorrowStream(&buf)
   100  	defer writePool.ReturnStream(stream)
   101  	WriteJSONStream(v, stream)
   102  	b := stream.Buffer()
   103  	err := stream.Flush()
   104  	// Help jsoniter manage its buffers--without this, the next
   105  	// use of the stream is likely to require an allocation. Look
   106  	// at the jsoniter stream code to understand why. They were probably
   107  	// optimizing for folks using the buffer directly.
   108  	stream.SetBuffer(b[:0])
   109  	return buf.Bytes(), err
   110  }
   111  
   112  // ReadJSONIter reads a Value from a JSON iterator.
   113  func ReadJSONIter(iter *jsoniter.Iterator) (Value, error) {
   114  	v := iter.Read()
   115  	if iter.Error != nil && iter.Error != io.EOF {
   116  		return nil, iter.Error
   117  	}
   118  	return NewValueInterface(v), nil
   119  }
   120  
   121  // WriteJSONStream writes a value into a JSON stream.
   122  func WriteJSONStream(v Value, stream *jsoniter.Stream) {
   123  	stream.WriteVal(v.Unstructured())
   124  }
   125  
   126  // ToYAML marshals a value as YAML.
   127  func ToYAML(v Value) ([]byte, error) {
   128  	return yaml.Marshal(v.Unstructured())
   129  }
   130  
   131  // Equals returns true iff the two values are equal.
   132  func Equals(lhs, rhs Value) bool {
   133  	return EqualsUsing(HeapAllocator, lhs, rhs)
   134  }
   135  
   136  // EqualsUsing uses the provided allocator and returns true iff the two values are equal.
   137  func EqualsUsing(a Allocator, lhs, rhs Value) bool {
   138  	if lhs.IsFloat() || rhs.IsFloat() {
   139  		var lf float64
   140  		if lhs.IsFloat() {
   141  			lf = lhs.AsFloat()
   142  		} else if lhs.IsInt() {
   143  			lf = float64(lhs.AsInt())
   144  		} else {
   145  			return false
   146  		}
   147  		var rf float64
   148  		if rhs.IsFloat() {
   149  			rf = rhs.AsFloat()
   150  		} else if rhs.IsInt() {
   151  			rf = float64(rhs.AsInt())
   152  		} else {
   153  			return false
   154  		}
   155  		return lf == rf
   156  	}
   157  	if lhs.IsInt() {
   158  		if rhs.IsInt() {
   159  			return lhs.AsInt() == rhs.AsInt()
   160  		}
   161  		return false
   162  	} else if rhs.IsInt() {
   163  		return false
   164  	}
   165  	if lhs.IsString() {
   166  		if rhs.IsString() {
   167  			return lhs.AsString() == rhs.AsString()
   168  		}
   169  		return false
   170  	} else if rhs.IsString() {
   171  		return false
   172  	}
   173  	if lhs.IsBool() {
   174  		if rhs.IsBool() {
   175  			return lhs.AsBool() == rhs.AsBool()
   176  		}
   177  		return false
   178  	} else if rhs.IsBool() {
   179  		return false
   180  	}
   181  	if lhs.IsList() {
   182  		if rhs.IsList() {
   183  			lhsList := lhs.AsListUsing(a)
   184  			defer a.Free(lhsList)
   185  			rhsList := rhs.AsListUsing(a)
   186  			defer a.Free(rhsList)
   187  			return lhsList.EqualsUsing(a, rhsList)
   188  		}
   189  		return false
   190  	} else if rhs.IsList() {
   191  		return false
   192  	}
   193  	if lhs.IsMap() {
   194  		if rhs.IsMap() {
   195  			lhsList := lhs.AsMapUsing(a)
   196  			defer a.Free(lhsList)
   197  			rhsList := rhs.AsMapUsing(a)
   198  			defer a.Free(rhsList)
   199  			return lhsList.EqualsUsing(a, rhsList)
   200  		}
   201  		return false
   202  	} else if rhs.IsMap() {
   203  		return false
   204  	}
   205  	if lhs.IsNull() {
   206  		if rhs.IsNull() {
   207  			return true
   208  		}
   209  		return false
   210  	} else if rhs.IsNull() {
   211  		return false
   212  	}
   213  	// No field is set, on either objects.
   214  	return true
   215  }
   216  
   217  // ToString returns a human-readable representation of the value.
   218  func ToString(v Value) string {
   219  	if v.IsNull() {
   220  		return "null"
   221  	}
   222  	switch {
   223  	case v.IsFloat():
   224  		return fmt.Sprintf("%v", v.AsFloat())
   225  	case v.IsInt():
   226  		return fmt.Sprintf("%v", v.AsInt())
   227  	case v.IsString():
   228  		return fmt.Sprintf("%q", v.AsString())
   229  	case v.IsBool():
   230  		return fmt.Sprintf("%v", v.AsBool())
   231  	case v.IsList():
   232  		strs := []string{}
   233  		list := v.AsList()
   234  		for i := 0; i < list.Length(); i++ {
   235  			strs = append(strs, ToString(list.At(i)))
   236  		}
   237  		return "[" + strings.Join(strs, ",") + "]"
   238  	case v.IsMap():
   239  		strs := []string{}
   240  		v.AsMap().Iterate(func(k string, v Value) bool {
   241  			strs = append(strs, fmt.Sprintf("%v=%v", k, ToString(v)))
   242  			return true
   243  		})
   244  		return strings.Join(strs, "")
   245  	}
   246  	// No field is set, on either objects.
   247  	return "{{undefined}}"
   248  }
   249  
   250  // Less provides a total ordering for Value (so that they can be sorted, even
   251  // if they are of different types).
   252  func Less(lhs, rhs Value) bool {
   253  	return Compare(lhs, rhs) == -1
   254  }
   255  
   256  // Compare provides a total ordering for Value (so that they can be
   257  // sorted, even if they are of different types). The result will be 0 if
   258  // v==rhs, -1 if v < rhs, and +1 if v > rhs.
   259  func Compare(lhs, rhs Value) int {
   260  	return CompareUsing(HeapAllocator, lhs, rhs)
   261  }
   262  
   263  // CompareUsing uses the provided allocator and provides a total
   264  // ordering for Value (so that they can be sorted, even if they
   265  // are of different types). The result will be 0 if v==rhs, -1
   266  // if v < rhs, and +1 if v > rhs.
   267  func CompareUsing(a Allocator, lhs, rhs Value) int {
   268  	if lhs.IsFloat() {
   269  		if !rhs.IsFloat() {
   270  			// Extra: compare floats and ints numerically.
   271  			if rhs.IsInt() {
   272  				return FloatCompare(lhs.AsFloat(), float64(rhs.AsInt()))
   273  			}
   274  			return -1
   275  		}
   276  		return FloatCompare(lhs.AsFloat(), rhs.AsFloat())
   277  	} else if rhs.IsFloat() {
   278  		// Extra: compare floats and ints numerically.
   279  		if lhs.IsInt() {
   280  			return FloatCompare(float64(lhs.AsInt()), rhs.AsFloat())
   281  		}
   282  		return 1
   283  	}
   284  
   285  	if lhs.IsInt() {
   286  		if !rhs.IsInt() {
   287  			return -1
   288  		}
   289  		return IntCompare(lhs.AsInt(), rhs.AsInt())
   290  	} else if rhs.IsInt() {
   291  		return 1
   292  	}
   293  
   294  	if lhs.IsString() {
   295  		if !rhs.IsString() {
   296  			return -1
   297  		}
   298  		return strings.Compare(lhs.AsString(), rhs.AsString())
   299  	} else if rhs.IsString() {
   300  		return 1
   301  	}
   302  
   303  	if lhs.IsBool() {
   304  		if !rhs.IsBool() {
   305  			return -1
   306  		}
   307  		return BoolCompare(lhs.AsBool(), rhs.AsBool())
   308  	} else if rhs.IsBool() {
   309  		return 1
   310  	}
   311  
   312  	if lhs.IsList() {
   313  		if !rhs.IsList() {
   314  			return -1
   315  		}
   316  		lhsList := lhs.AsListUsing(a)
   317  		defer a.Free(lhsList)
   318  		rhsList := rhs.AsListUsing(a)
   319  		defer a.Free(rhsList)
   320  		return ListCompareUsing(a, lhsList, rhsList)
   321  	} else if rhs.IsList() {
   322  		return 1
   323  	}
   324  	if lhs.IsMap() {
   325  		if !rhs.IsMap() {
   326  			return -1
   327  		}
   328  		lhsMap := lhs.AsMapUsing(a)
   329  		defer a.Free(lhsMap)
   330  		rhsMap := rhs.AsMapUsing(a)
   331  		defer a.Free(rhsMap)
   332  		return MapCompareUsing(a, lhsMap, rhsMap)
   333  	} else if rhs.IsMap() {
   334  		return 1
   335  	}
   336  	if lhs.IsNull() {
   337  		if !rhs.IsNull() {
   338  			return -1
   339  		}
   340  		return 0
   341  	} else if rhs.IsNull() {
   342  		return 1
   343  	}
   344  
   345  	// Invalid Value-- nothing is set.
   346  	return 0
   347  }
   348  

View as plain text