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  }