github.com/grailbio/bigslice@v0.0.0-20230519005545-30c4c12152ad/slicetype/slicetype.go (about) 1 // Copyright 2018 GRAIL, Inc. All rights reserved. 2 // Use of this source code is governed by the Apache 2.0 3 // license that can be found in the LICENSE file. 4 5 // Package slicetype implements data types and utilities to describe 6 // Bigslice types: Slices, Frames, and Tasks all carry 7 // slicetype.Types. 8 package slicetype 9 10 import ( 11 "fmt" 12 "reflect" 13 "strings" 14 ) 15 16 // A Type is the type of a set of columns. 17 type Type interface { 18 // NumOut returns the number of columns. 19 NumOut() int 20 // Out returns the data type of the ith column. 21 Out(i int) reflect.Type 22 // Prefix returns the number of columns in the type 23 // which are considered the type's prefix. A type's 24 // prefix is the set of columns which are considered 25 // the type's key columns for operations like reduce. 26 Prefix() int 27 } 28 29 type typeSlice []reflect.Type 30 31 // New returns a new Type using the provided column types. 32 func New(types ...reflect.Type) Type { 33 return typeSlice(types) 34 } 35 36 func (t typeSlice) NumOut() int { return len(t) } 37 func (t typeSlice) Out(i int) reflect.Type { return t[i] } 38 func (t typeSlice) Prefix() int { return 1 } 39 40 // Assignable reports whether column type in can be 41 // assigned to out. 42 func Assignable(in, out Type) bool { 43 if in.NumOut() != out.NumOut() { 44 return false 45 } 46 for i := 0; i < in.NumOut(); i++ { 47 if !in.Out(i).AssignableTo(out.Out(i)) { 48 return false 49 } 50 } 51 return true 52 } 53 54 // Columns returns a slice of column types from the provided type. 55 func Columns(typ Type) []reflect.Type { 56 if slice, ok := typ.(typeSlice); ok { 57 return slice 58 } 59 out := make([]reflect.Type, typ.NumOut()) 60 for i := range out { 61 out[i] = typ.Out(i) 62 } 63 return out 64 } 65 66 func Concat(types ...Type) Type { 67 var t typeSlice 68 for _, typ := range types { 69 t = append(t, Columns(typ)...) 70 } 71 return t 72 } 73 74 func String(typ Type) string { 75 elems := make([]string, typ.NumOut()) 76 for i := range elems { 77 elems[i] = typ.Out(i).String() 78 } 79 return fmt.Sprintf("slice[%d]%s", typ.Prefix(), strings.Join(elems, ",")) 80 } 81 82 type appendType struct { 83 t1, t2 Type 84 } 85 86 func (a appendType) NumOut() int { 87 return a.t1.NumOut() + a.t2.NumOut() 88 } 89 90 func (a appendType) Out(i int) reflect.Type { 91 if i < a.t1.NumOut() { 92 return a.t1.Out(i) 93 } 94 return a.t2.Out(i - a.t1.NumOut()) 95 } 96 97 func (a appendType) Prefix() int { return a.t1.Prefix() } 98 99 func Append(t1, t2 Type) Type { 100 return appendType{t1, t2} 101 } 102 103 type sliceType struct { 104 t Type 105 i, j int 106 } 107 108 func (s sliceType) NumOut() int { 109 return s.j - s.i 110 } 111 112 func (s sliceType) Out(i int) reflect.Type { 113 if i >= s.NumOut() { 114 panic("invalid index") 115 } 116 return s.t.Out(s.i + i) 117 } 118 119 // BUG(marius): prefixes are lost when slicing a type. 120 func (s sliceType) Prefix() int { 121 // TODO(marius): figure out how to properly compute 122 // prefixes for appended types and sliced types. 123 // This is currently only used in places which do not 124 // accept prefixes anyway. 125 return 1 126 } 127 128 func Slice(t Type, i, j int) Type { 129 if i < 0 || i > t.NumOut() || j < i || j > t.NumOut() { 130 panic("slice: invalid argument") 131 } 132 return sliceType{t, i, j} 133 } 134 135 // Signature returns a Go function signature for a function that takes the 136 // provided arguments and returns the provided values. 137 func Signature(arg, ret Type) string { 138 args := make([]string, arg.NumOut()) 139 for i := range args { 140 args[i] = arg.Out(i).String() 141 } 142 rets := make([]string, ret.NumOut()) 143 for i := range rets { 144 rets[i] = ret.Out(i).String() 145 } 146 var b strings.Builder 147 b.WriteString("func(") 148 b.WriteString(strings.Join(args, ", ")) 149 b.WriteString(")") 150 switch len(rets) { 151 case 0: 152 case 1: 153 b.WriteString(" ") 154 b.WriteString(rets[0]) 155 default: 156 b.WriteString(" (") 157 b.WriteString(strings.Join(rets, ", ")) 158 b.WriteString(")") 159 } 160 return b.String() 161 }