github.com/tobgu/qframe@v0.4.0/internal/icolumn/column.go (about) 1 package icolumn 2 3 import ( 4 "github.com/tobgu/qframe/internal/column" 5 "github.com/tobgu/qframe/internal/hash" 6 "github.com/tobgu/qframe/internal/index" 7 "github.com/tobgu/qframe/qerrors" 8 "github.com/tobgu/qframe/types" 9 "reflect" 10 "strconv" 11 "unsafe" 12 ) 13 14 func (c Column) DataType() types.DataType { 15 return types.Int 16 } 17 18 func (c Column) StringAt(i uint32, _ string) string { 19 return strconv.FormatInt(int64(c.data[i]), 10) 20 } 21 22 func (c Column) AppendByteStringAt(buf []byte, i uint32) []byte { 23 return strconv.AppendInt(buf, int64(c.data[i]), 10) 24 } 25 26 func (c Column) ByteSize() int { 27 // Slice header + data 28 return 2*8 + 8*cap(c.data) 29 } 30 31 func (c Column) Equals(index index.Int, other column.Column, otherIndex index.Int) bool { 32 otherI, ok := other.(Column) 33 if !ok { 34 return false 35 } 36 37 for ix, x := range index { 38 if c.data[x] != otherI.data[otherIndex[ix]] { 39 return false 40 } 41 } 42 43 return true 44 } 45 46 func (c Column) FloatSlice() []float64 { 47 result := make([]float64, len(c.data)) 48 for i, v := range c.data { 49 result[i] = float64(v) 50 } 51 52 return result 53 } 54 55 func (c Comparable) Compare(i, j uint32) column.CompareResult { 56 x, y := c.data[i], c.data[j] 57 if x < y { 58 return c.ltValue 59 } 60 61 if x > y { 62 return c.gtValue 63 } 64 65 return column.Equal 66 } 67 68 func (c Comparable) Hash(i uint32, seed uint64) uint64 { 69 x := &c.data[i] 70 b := (*[8]byte)(unsafe.Pointer(x))[:] 71 return hash.HashBytes(b, seed) 72 } 73 74 func intComp(comparatee interface{}) (int, bool) { 75 comp, ok := comparatee.(int) 76 if !ok { 77 // Accept floats by truncating them 78 compFloat, ok := comparatee.(float64) 79 if !ok { 80 return 0, false 81 } 82 comp = int(compFloat) 83 } 84 85 return comp, true 86 } 87 88 type intSet map[int]struct{} 89 90 func interfaceSliceToIntSlice(ss []interface{}) ([]int, bool) { 91 result := make([]int, len(ss)) 92 for i, s := range ss { 93 switch t := s.(type) { 94 case int: 95 result[i] = t 96 case float64: 97 result[i] = int(t) 98 default: 99 return nil, false 100 } 101 } 102 return result, true 103 } 104 105 func newIntSet(input interface{}) (intSet, bool) { 106 var result intSet 107 var ok bool 108 switch t := input.(type) { 109 case []int: 110 result, ok = make(intSet, len(t)), true 111 for _, v := range t { 112 result[v] = struct{}{} 113 } 114 case []float64: 115 result, ok = make(intSet, len(t)), true 116 for _, v := range t { 117 result[int(v)] = struct{}{} 118 } 119 case []interface{}: 120 if intSlice, innerOk := interfaceSliceToIntSlice(t); innerOk { 121 result, ok = newIntSet(intSlice) 122 } 123 } 124 125 return result, ok 126 } 127 128 func (is intSet) Contains(x int) bool { 129 _, ok := is[x] 130 return ok 131 } 132 133 func (c Column) filterBuiltIn(index index.Int, comparator string, comparatee interface{}, bIndex index.Bool) error { 134 if intC, ok := intComp(comparatee); ok { 135 filterFn, ok := filterFuncs[comparator] 136 if !ok { 137 return qerrors.New("filter int", "unknown filter operator %v", comparator) 138 } 139 filterFn(index, c.data, intC, bIndex) 140 } else if set, ok := newIntSet(comparatee); ok { 141 filterFn, ok := multiInputFilterFuncs[comparator] 142 if !ok { 143 return qerrors.New("filter int", "unknown filter operator %v", comparator) 144 } 145 filterFn(index, c.data, set, bIndex) 146 } else if columnC, ok := comparatee.(Column); ok { 147 filterFn, ok := filterFuncs2[comparator] 148 if !ok { 149 return qerrors.New("filter int", "unknown filter operator %v", comparator) 150 } 151 filterFn(index, c.data, columnC.data, bIndex) 152 } else if comparatee == nil { 153 compFunc, ok := filterFuncs0[comparator] 154 if !ok { 155 return qerrors.New("filter int", "invalid comparison operator to zero argument filter, %v", comparator) 156 } 157 compFunc(index, c.data, bIndex) 158 } else { 159 return qerrors.New("filter int", "invalid comparison value type %v", reflect.TypeOf(comparatee)) 160 } 161 162 return nil 163 } 164 165 func (c Column) filterCustom1(index index.Int, fn func(int) bool, bIndex index.Bool) { 166 for i, x := range bIndex { 167 if !x { 168 bIndex[i] = fn(c.data[index[i]]) 169 } 170 } 171 } 172 173 func (c Column) filterCustom2(index index.Int, fn func(int, int) bool, comparatee interface{}, bIndex index.Bool) error { 174 otherC, ok := comparatee.(Column) 175 if !ok { 176 return qerrors.New("filter int", "expected comparatee to be int column, was %v", reflect.TypeOf(comparatee)) 177 } 178 179 for i, x := range bIndex { 180 if !x { 181 bIndex[i] = fn(c.data[index[i]], otherC.data[index[i]]) 182 } 183 } 184 185 return nil 186 } 187 188 func (c Column) Filter(index index.Int, comparator interface{}, comparatee interface{}, bIndex index.Bool) error { 189 var err error 190 switch t := comparator.(type) { 191 case string: 192 err = c.filterBuiltIn(index, t, comparatee, bIndex) 193 case func(int) bool: 194 c.filterCustom1(index, t, bIndex) 195 case func(int, int) bool: 196 err = c.filterCustom2(index, t, comparatee, bIndex) 197 default: 198 err = qerrors.New("filter int", "invalid filter type %v", reflect.TypeOf(comparator)) 199 } 200 return err 201 } 202 203 func (c Column) FunctionType() types.FunctionType { 204 return types.FunctionTypeInt 205 } 206 207 func (c Column) Append(cols ...column.Column) (column.Column, error) { 208 // TODO Improve, currently copies all data over to a new column, this may not be the best solution... 209 newLen := c.Len() 210 intCols := append(make([]Column, 0, len(cols)+1), c) 211 for _, col := range cols { 212 intCol, ok := col.(Column) 213 if !ok { 214 return nil, qerrors.New("append int", "can only append integer columns to integer column") 215 } 216 newLen += intCol.Len() 217 intCols = append(intCols, intCol) 218 } 219 220 newData := make([]int, newLen) 221 offset := 0 222 for _, col := range intCols { 223 offset += copy(newData[offset:], col.data) 224 } 225 226 return New(newData), nil 227 }