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 }