github.com/mitranim/gg@v0.1.17/comparison.go (about)

     1  package gg
     2  
     3  import (
     4  	r "reflect"
     5  	u "unsafe"
     6  )
     7  
     8  /*
     9  Returns true if the input is the zero value of its type. Optionally falls back
    10  on `Zeroable.IsZero` if the input implements this interface. Support for
    11  `Zeroable` is useful for types such as `time.Time`, where "zero" is determined
    12  only by the timestamp, ignoring the timezone.
    13  */
    14  func IsZero[A any](val A) bool {
    15  	box := AnyNoEscUnsafe(val)
    16  	if box == nil {
    17  		return true
    18  	}
    19  
    20  	/**
    21  	When possible, we want to "natively" compare to a zero value before invoking
    22  	`Zeroable`. Depending on the Go version, this may slightly speed us up, or
    23  	slightly slow us down. More importantly, this improves correctness,
    24  	safeguarding us against bizarre implementations of `Zeroable` such as
    25  	`reflect.Value.IsZero`, which panics when called on the zero value of
    26  	`reflect.Value`.
    27  
    28  	It would be ideal to compare concrete values rather than `any`, but requiring
    29  	`comparable` would make this function much less usable, and comparing `any`
    30  	seems fast enough.
    31  	*/
    32  	if Type[A]().Comparable() {
    33  		if box == AnyNoEscUnsafe(Zero[A]()) {
    34  			return true
    35  		}
    36  
    37  		/**
    38  		Terminate here to avoid falling down to the reflection-based clause.
    39  		We already know that our value is not considered zero by Go.
    40  		*/
    41  		impl, _ := box.(Zeroable)
    42  		return impl != nil && impl.IsZero()
    43  	}
    44  
    45  	impl, _ := box.(Zeroable)
    46  	if impl != nil {
    47  		return impl.IsZero()
    48  	}
    49  
    50  	/**
    51  	Implementation note. For some types, some non-zero byte patterns are
    52  	considered a zero value. The most notable example is strings. For example,
    53  	the following expression makes a string with a non-zero data pointer, which
    54  	is nevertheless considered a zero value by Go:
    55  
    56  		`some_text`[:0]
    57  
    58  	`reflect.Value.IsZero` correctly handles such cases, and doesn't seem to be
    59  	outrageously slow.
    60  	*/
    61  	return r.ValueOf(box).IsZero()
    62  }
    63  
    64  // Inverse of `IsZero`.
    65  func IsNotZero[A any](val A) bool { return !IsZero(val) }
    66  
    67  /*
    68  True if every byte in the given value is zero. Not equivalent to `IsZero`.
    69  Most of the time, you should prefer `IsZero`, which is more performant and
    70  more correct.
    71  */
    72  func IsTrueZero[A any](val A) bool {
    73  	size := u.Sizeof(val)
    74  	for off := uintptr(0); off < size; off++ {
    75  		if *(*byte)(u.Pointer(uintptr(u.Pointer(&val)) + off)) != 0 {
    76  			return false
    77  		}
    78  	}
    79  	return true
    80  }
    81  
    82  // Generic variant of `Nullable.IsNull`.
    83  func IsNull[A Nullable](val A) bool { return val.IsNull() }
    84  
    85  // Inverse of `IsNull`.
    86  func IsNotNull[A Nullable](val A) bool { return !val.IsNull() }
    87  
    88  /*
    89  True if the inputs are byte-for-byte identical. This function is not meant for
    90  common use. Nearly always, you should use `Eq` or `Equal` instead. This one is
    91  sometimes useful for testing purposes, such as asserting that two interface
    92  values refer to the same underlying data. This may lead to brittle code that is
    93  not portable between different Go implementations. Performance is similar to
    94  `==` for small value sizes (up to 2-4 machine words) but is significantly worse
    95  for large value sizes.
    96  */
    97  func Is[A any](one, two A) bool {
    98  	/**
    99  	Note. The "ideal" implementation looks like this:
   100  
   101  		const size = u.Sizeof(one)
   102  		return CastUnsafe[[size]byte](one) == CastUnsafe[[size]byte](two)
   103  
   104  	But at the time of writing, in Go 1.19, `unsafe.Sizeof` on a type parameter
   105  	is considered non-constant. If this changes in the future, we'll switch to
   106  	the implementation above.
   107  	*/
   108  
   109  	size := u.Sizeof(one)
   110  
   111  	switch size {
   112  	case 0:
   113  		return true
   114  
   115  	case SizeofWord:
   116  		return *(*uint)(u.Pointer(&one)) == *(*uint)(u.Pointer(&two))
   117  
   118  	// Common case: comparing interfaces or strings.
   119  	case SizeofWord * 2:
   120  		return (*(*uint)(u.Pointer(&one)) == *(*uint)(u.Pointer(&two))) &&
   121  			(*(*uint)(u.Pointer(uintptr(u.Pointer(&one)) + SizeofWord)) ==
   122  				*(*uint)(u.Pointer(uintptr(u.Pointer(&two)) + SizeofWord)))
   123  
   124  	// Common case: comparing slices.
   125  	case SizeofWord * 3:
   126  		return (*(*uint)(u.Pointer(&one)) == *(*uint)(u.Pointer(&two))) &&
   127  			(*(*uint)(u.Pointer(uintptr(u.Pointer(&one)) + SizeofWord)) ==
   128  				*(*uint)(u.Pointer(uintptr(u.Pointer(&two)) + SizeofWord))) &&
   129  			(*(*uint)(u.Pointer(uintptr(u.Pointer(&one)) + SizeofWord*2)) ==
   130  				*(*uint)(u.Pointer(uintptr(u.Pointer(&two)) + SizeofWord*2)))
   131  
   132  	default:
   133  		/**
   134  		Implementation note. We could also walk word-by-word by using padded structs
   135  		to ensure sufficient empty memory. It would improve the performance
   136  		slightly, but not enough to bother. The resulting performance is still
   137  		much worse than `==` on whole values.
   138  		*/
   139  		for off := uintptr(0); off < size; off++ {
   140  			oneChunk := *(*byte)(u.Pointer(uintptr(u.Pointer(&one)) + off))
   141  			twoChunk := *(*byte)(u.Pointer(uintptr(u.Pointer(&two)) + off))
   142  			if oneChunk != twoChunk {
   143  				return false
   144  			}
   145  		}
   146  		return true
   147  	}
   148  }
   149  
   150  // Same as `==`. Sometimes useful with higher-order functions.
   151  func Eq[A comparable](one, two A) bool { return one == two }
   152  
   153  /*
   154  Short for "equal". For types that implement `Equaler`, this simply calls their
   155  equality method. Otherwise falls back on `reflect.DeepEqual`. Compared to
   156  `reflect.DeepEqual`, this has better type safety, and in many cases this has
   157  better performance, even when calling `reflect.DeepEqual` in fallback mode.
   158  */
   159  func Equal[A any](one, two A) bool {
   160  	impl, _ := AnyNoEscUnsafe(one).(Equaler[A])
   161  	if impl != nil {
   162  		return impl.Equal(two)
   163  	}
   164  	return r.DeepEqual(AnyNoEscUnsafe(one), AnyNoEscUnsafe(two))
   165  }
   166  
   167  /*
   168  True if the inputs are equal via `==`, and neither is a zero value of its type.
   169  For non-equality, use `NotEqNotZero`.
   170  */
   171  func EqNotZero[A comparable](one, two A) bool {
   172  	return one == two && one != Zero[A]()
   173  }
   174  
   175  /*
   176  True if the inputs are non-equal via `!=`, and at least one is not a zero value
   177  of its type. For equality, use `EqNotZero`.
   178  */
   179  func NotEqNotZero[A comparable](one, two A) bool {
   180  	return one != two && one != Zero[A]()
   181  }
   182  
   183  /*
   184  True if the given slice headers are byte-for-byte identical. In other words,
   185  true if the given slices have the same data pointer, length, capacity. Does not
   186  compare individual elements.
   187  */
   188  func SliceIs[A any](one, two []A) bool {
   189  	return CastUnsafe[r.SliceHeader](one) == CastUnsafe[r.SliceHeader](two)
   190  }
   191  
   192  // Returns the first non-zero value from among the inputs.
   193  func Or[A any](val ...A) A { return Find(val, IsNotZero[A]) }
   194  
   195  /*
   196  Variant of `Or` compatible with `Nullable`. Returns the first non-"null" value
   197  from among the inputs.
   198  */
   199  func NullOr[A Nullable](val ...A) A { return Find(val, IsNotNull[A]) }
   200  
   201  // Version of `<` for non-primitives that implement `Lesser`.
   202  func Less2[A Lesser[A]](one, two A) bool { return one.Less(two) }
   203  
   204  /*
   205  Variadic version of `<` for non-primitives that implement `Lesser`.
   206  Shortcut for `IsSorted` with `Less2`.
   207  */
   208  func Less[A Lesser[A]](src ...A) bool { return IsSorted(src, Less2[A]) }
   209  
   210  // Same as Go's `<` operator, expressed as a generic function.
   211  func LessPrim2[A LesserPrim](one, two A) bool { return one < two }
   212  
   213  /*
   214  Variadic version of `<` for non-primitives that implement `Lesser`.
   215  Shortcut for `IsSorted` with `LessPrim2`.
   216  */
   217  func LessPrim[A LesserPrim](src ...A) bool { return IsSorted(src, LessPrim2[A]) }
   218  
   219  // Version of `<=` for non-primitives that implement `Lesser`.
   220  func LessEq2[A Lesser[A]](one, two A) bool { return one.Less(two) || Equal(one, two) }
   221  
   222  /*
   223  Variadic version of `<=` for non-primitives that implement `Lesser`.
   224  Shortcut for `IsSorted` with `LessEq2`.
   225  */
   226  func LessEq[A Lesser[A]](src ...A) bool { return IsSorted(src, LessEq2[A]) }
   227  
   228  // Same as Go's `<=` operator, expressed as a generic function.
   229  func LessEqPrim2[A LesserPrim](one, two A) bool { return one <= two }
   230  
   231  /*
   232  Variadic version of Go's `<=` operator.
   233  Shortcut for `IsSorted` with `LessEqPrim2`.
   234  */
   235  func LessEqPrim[A LesserPrim](src ...A) bool { return IsSorted(src, LessEqPrim2[A]) }
   236  
   237  /*
   238  Returns the lesser of the two inputs. For primitive types that don't implement
   239  `Lesser`, see `MinPrim2`. For a variadic variant, see `Min`.
   240  */
   241  func Min2[A Lesser[A]](one, two A) A {
   242  	if one.Less(two) {
   243  		return one
   244  	}
   245  	return two
   246  }
   247  
   248  /*
   249  Returns the lesser of the two inputs, which must be comparable primitives. For
   250  non-primitives, see `Min2`. For a variadic variant, see `MinPrim`.
   251  */
   252  func MinPrim2[A LesserPrim](one, two A) A {
   253  	if one < two {
   254  		return one
   255  	}
   256  	return two
   257  }
   258  
   259  /*
   260  Returns the larger of the two inputs. For primitive types that don't implement
   261  `Lesser`, see `MaxPrim2`. For a variadic variant, see `Max`.
   262  */
   263  func Max2[A Lesser[A]](one, two A) A {
   264  	if one.Less(two) {
   265  		return two
   266  	}
   267  	return one
   268  }
   269  
   270  /*
   271  Returns the larger of the two inputs, which must be comparable primitives. For
   272  non-primitives, see `Max2`. For a variadic variant, see `MaxPrim`.
   273  */
   274  func MaxPrim2[A LesserPrim](one, two A) A {
   275  	if one < two {
   276  		return two
   277  	}
   278  	return one
   279  }