utils.go (151 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 iceberg
import (
"cmp"
"fmt"
"hash/maphash"
"maps"
"runtime/debug"
"strings"
)
var version string
func init() {
version = "(unknown version)"
if info, ok := debug.ReadBuildInfo(); ok {
for _, dep := range info.Deps {
if strings.HasPrefix(dep.Path, "github.com/apache/iceberg-go") {
version = dep.Version
break
}
}
}
}
func Version() string { return version }
func max[T cmp.Ordered](vals ...T) T {
if len(vals) == 0 {
panic("can't call max with no arguments")
}
out := vals[0]
for _, v := range vals[1:] {
if v > out {
out = v
}
}
return out
}
// Optional represents a typed value that could be null
type Optional[T any] struct {
Val T
Valid bool
}
// represents a single row in a record
type structLike interface {
// Size returns the number of columns in this row
Size() int
// Get returns the value in the requested column,
// will panic if pos is out of bounds.
Get(pos int) any
// Set changes the value in the column indicated,
// will panic if pos is out of bounds.
Set(pos int, val any)
}
type accessor struct {
pos int
inner *accessor
}
func (a *accessor) String() string {
return fmt.Sprintf("Accessor(position=%d, inner=%s)", a.pos, a.inner)
}
func (a *accessor) Get(s structLike) any {
val, inner := s.Get(a.pos), a
for val != nil && inner.inner != nil {
inner = inner.inner
val = val.(structLike).Get(inner.pos)
}
return val
}
type Set[E any] interface {
Add(...E)
Contains(E) bool
Members() []E
Equals(Set[E]) bool
Len() int
All(func(E) bool) bool
}
var lzseed = maphash.MakeSeed()
type literalSet map[any]struct{ orig Literal }
func newLiteralSet(vals ...Literal) Set[Literal] {
s := literalSet{}
for _, v := range vals {
s.addliteral(v)
}
return s
}
func (l literalSet) addliteral(v Literal) {
switch v := v.(type) {
case FixedLiteral:
l[maphash.Bytes(lzseed, []byte(v))] = struct{ orig Literal }{v}
case BinaryLiteral:
l[maphash.Bytes(lzseed, []byte(v))] = struct{ orig Literal }{v}
default:
l[v] = struct{ orig Literal }{}
}
}
func (l literalSet) Add(lits ...Literal) {
for _, v := range lits {
l.addliteral(v)
}
}
func (l literalSet) Contains(lit Literal) bool {
switch lit := lit.(type) {
case BinaryLiteral:
v, ok := l[maphash.Bytes(lzseed, []byte(lit))]
if !ok {
return false
}
return lit.Equals(v.orig)
case FixedLiteral:
v, ok := l[maphash.Bytes(lzseed, []byte(lit))]
if !ok {
return false
}
return lit.Equals(v.orig)
default:
_, ok := l[lit]
return ok
}
}
func (l literalSet) Members() []Literal {
result := make([]Literal, 0, len(l))
for k, v := range l {
if k, ok := k.(Literal); ok {
result = append(result, k)
} else {
result = append(result, v.orig)
}
}
return result
}
func (l literalSet) Equals(other Set[Literal]) bool {
rhs, ok := other.(literalSet)
if !ok {
return false
}
return maps.EqualFunc(l, rhs, func(v1, v2 struct{ orig Literal }) bool {
switch {
case v1.orig == nil:
return v2.orig == nil
case v2.orig == nil:
return v1.orig == nil
default:
return v1.orig.Equals(v2.orig)
}
})
}
func (l literalSet) Len() int { return len(l) }
func (l literalSet) All(fn func(Literal) bool) bool {
for k, v := range l {
var e Literal
if k, ok := k.(Literal); ok {
e = k
} else {
e = v.orig
}
if !fn(e) {
return false
}
}
return true
}