├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cmd └── peds │ └── main.go ├── examples ├── collections.go └── types.go ├── experience_report.md ├── go.mod ├── go.sum ├── go2go └── src │ └── peds │ ├── containers.go │ ├── containers.go2 │ ├── map.go │ ├── map.go2 │ ├── map_test.go │ ├── map_test.go2 │ ├── vector_test.go │ └── vector_test.go2 ├── internal ├── generic_types │ ├── containers.go │ ├── functions.go │ └── types.go └── templates │ └── templates.go ├── tests ├── map_test.go ├── set_test.go ├── subpackage │ └── types.go ├── subpackage2 │ └── README.md ├── types.go └── vector_test.go └── util └── create_templates.py /.gitignore: -------------------------------------------------------------------------------- 1 | *_gen.go 2 | .idea 3 | coverage.* 4 | /peds 5 | *.test 6 | *.out 7 | *.prof 8 | *.mprof 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Tobias Gustafsson 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | dev_generate: 2 | python util/create_templates.py 3 | 4 | build: dev_generate 5 | go build github.com/tobgu/peds/cmd/peds/ 6 | 7 | install: dev_generate 8 | go install ./cmd/peds 9 | 10 | fmt: 11 | go fmt ./... 12 | 13 | test: install 14 | rm tests/*_gen.go || echo "No previous test files" 15 | cd tests && go generate 16 | go test ./... 17 | 18 | go2go-test: 19 | GO2PATH=$$(pwd)/go2go go2go test peds 20 | 21 | benchmark_vector: 22 | rm tests/*_gen.go 23 | cd tests && go generate && go test -bench Iteration -run=^$ 24 | 25 | benchmark_map: 26 | rm tests/*_gen.go 27 | cd tests && go generate && go test -bench Map -run=^$ 28 | 29 | examples: 30 | cd examples && go generate 31 | 32 | .PHONY: dev_generate build install fmt test benchmark_vector benchmark_map examples 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Statically type safe persistent/immutable/functional data structures for Go. 2 | 3 | Inspired by Clojures data structures and the work done in 4 | [Pyrsistent](https://www.github.com/tobgu/pyrsistent) for Python. 5 | 6 | This is an experiment in how close to generics that code generation can take 7 | you. There's currently a vector, a slice, a map and a set implemented. 8 | 9 | ## What's a persistent data structure? 10 | Despite their name persistent data structures usually don't refer to 11 | data structures stored on disk. Instead they are immutable data 12 | structures which are copy-on-write. Eg. whenever you mutate a persistent 13 | data structure a new one is created that contains the data in the 14 | original plus the mutation while the original is left untouched. 15 | 16 | To make this reasonably efficient, "structural sharing" is used between 17 | the data structures. Here's a good introduction to how this is done in 18 | Clojure: 19 | http://hypirion.com/musings/understanding-persistent-vector-pt-1 20 | 21 | ## Installation 22 | `go get github.com/tobgu/peds/cmd/peds` 23 | 24 | ## Usage 25 | ``` 26 | Generate statically type safe code for persistent data structures. 27 | 28 | USAGE 29 | peds 30 | 31 | FLAGS EXAMPLE 32 | -file path/to/file.go 33 | -imports import1;import2 34 | -maps Map1;Map2 35 | -pkg package_name 36 | -sets Set1 37 | -vectors Vec1 38 | ``` 39 | 40 | ## Examples 41 | 42 | There are a couple of generated example collections in 43 | [examples/collections.go](https://github.com/tobgu/peds/blob/master/examples/collections.go). 44 | 45 | The `go:generate` command used can be found in [examples/types.go](https://github.com/tobgu/peds/blob/master/examples/types.go). 46 | 47 | This illustrates the core usage pattern: 48 | ``` 49 | //go:generate peds -vectors=IntVector -pkg=my_collections -file=collections/my_collections_gen.go 50 | 51 | // Create a new vector 52 | v := my_collections.NewIntVector(1, 2, 3) 53 | 54 | // Create a copy of v with the first element set to 55, v is left untouched. 55 | v2 := v.Set(0, 55) 56 | ``` 57 | 58 | ## Godoc 59 | 60 | #### Generic types 61 | https://godoc.org/github.com/tobgu/peds/internal/generic_types 62 | 63 | #### Generated examples 64 | https://godoc.org/github.com/tobgu/peds/examples 65 | 66 | ## Experiences 67 | 68 | There's an [experience report](https://github.com/tobgu/peds/blob/master/experience_report.md) based on the implementation of this library. 69 | 70 | ## Caveats 71 | * Even though the data structures are immutable by most means it is not 72 | possible to use them as keys in hash maps for example. This is because 73 | they internally make use of slices, which are not comparable in Go. 74 | 75 | ## Possible improvements 76 | * Investigate implementing the Map as a CHAMP tree. 77 | * Introspection of the contained types possible to 78 | refine the hash functions? 79 | * Get rid of Python dependency for developing peds (not needed to build or use peds). 80 | 81 | ## Regenerate templates and run tests 82 | `make test` 83 | 84 | ## Want to contribute? 85 | Great! Write an issue and let the discussions begin! Then file a PR! 86 | -------------------------------------------------------------------------------- /cmd/peds/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "go/format" 8 | "io" 9 | "os" 10 | "regexp" 11 | "strings" 12 | "text/tabwriter" 13 | "text/template" 14 | 15 | "github.com/pkg/errors" 16 | "github.com/tobgu/peds/internal/templates" 17 | ) 18 | 19 | func usage(fs *flag.FlagSet) func() { 20 | return func() { 21 | os.Stderr.WriteString("Generate statically type safe code for persistent data structures.\n\n") 22 | os.Stderr.WriteString("USAGE\n") 23 | os.Stderr.WriteString("peds\n\n") 24 | os.Stderr.WriteString("FLAGS EXAMPLE\n") 25 | w := tabwriter.NewWriter(os.Stderr, 0, 2, 2, ' ', 0) 26 | fs.VisitAll(func(f *flag.Flag) { 27 | fmt.Fprintf(w, "\t-%s %s\t%s\n", f.Name, f.DefValue, f.Usage) 28 | }) 29 | w.Flush() 30 | os.Stderr.WriteString("\n") 31 | } 32 | } 33 | 34 | func logAndExit(err error, flagSet *flag.FlagSet) { 35 | fmt.Fprint(os.Stderr, "Error: ", err, "\n\n") 36 | flagSet.Usage() 37 | os.Exit(1) 38 | } 39 | 40 | func main() { 41 | flagSet := flag.NewFlagSet("server", flag.ExitOnError) 42 | var ( 43 | vectors = flagSet.String("vectors", "", "Vec1") 44 | maps = flagSet.String("maps", "", "Map1;Map2") 45 | sets = flagSet.String("sets", "", "Set1") 46 | file = flagSet.String("file", "", "path/to/file.go") 47 | imports = flagSet.String("imports", "", "import1;import2") 48 | pkg = flagSet.String("pkg", "", "package_name") 49 | ) 50 | 51 | flagSet.Usage = usage(flagSet) 52 | if err := flagSet.Parse(os.Args[1:]); err != nil { 53 | logAndExit(err, flagSet) 54 | } 55 | 56 | buf := &bytes.Buffer{} 57 | 58 | if err := renderCommon(buf, *pkg, *imports); err != nil { 59 | logAndExit(err, flagSet) 60 | } 61 | 62 | if err := renderVectors(buf, *vectors); err != nil { 63 | logAndExit(err, flagSet) 64 | } 65 | 66 | if err := renderMaps(buf, *maps); err != nil { 67 | logAndExit(err, flagSet) 68 | } 69 | 70 | if err := renderSet(buf, *sets); err != nil { 71 | logAndExit(err, flagSet) 72 | } 73 | 74 | if err := writeFile(buf, *file); err != nil { 75 | logAndExit(err, flagSet) 76 | } 77 | } 78 | 79 | /////////////// 80 | /// Helpers /// 81 | /////////////// 82 | 83 | func removeWhiteSpaces(s string) string { 84 | return strings.Join(strings.Fields(s), "") 85 | } 86 | 87 | type templateSpec struct { 88 | name string 89 | template string 90 | } 91 | 92 | func renderTemplates(specs []templateSpec, templateData interface{}, dst io.Writer) error { 93 | for _, s := range specs { 94 | t := template.New(s.name) 95 | t, err := t.Parse(s.template) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | err = t.Execute(dst, templateData) 101 | if err != nil { 102 | return err 103 | } 104 | } 105 | 106 | return nil 107 | } 108 | 109 | ////////////// 110 | /// Common /// 111 | ////////////// 112 | 113 | func renderCommon(buf *bytes.Buffer, pkgName, importsString string) error { 114 | pkgName = removeWhiteSpaces(pkgName) 115 | if pkgName == "" { 116 | return errors.New("pkg is required") 117 | } 118 | 119 | importTemplate := `{{if .Imports}} 120 | import ( 121 | {{range $imp := .Imports}} 122 | "{{$imp}}" 123 | {{end}} 124 | ) 125 | {{end}}` 126 | 127 | var imports []string = nil 128 | importsString = removeWhiteSpaces(importsString) 129 | if importsString != "" { 130 | imports = strings.Split(importsString, ";") 131 | } 132 | 133 | return renderTemplates([]templateSpec{ 134 | {name: "pkg", template: "package {{index .PackageName 0}}\n"}, 135 | {name: "imports", template: importTemplate}, 136 | {name: "common", template: templates.CommonTemplate}}, 137 | map[string][]string{"PackageName": {pkgName}, "Imports": imports}, buf) 138 | } 139 | 140 | ////////////// 141 | /// Vector /// 142 | ////////////// 143 | 144 | func renderVectors(buf *bytes.Buffer, vectors string) error { 145 | vectors = removeWhiteSpaces(vectors) 146 | if vectors == "" { 147 | return nil 148 | } 149 | 150 | vectorSpecs, err := parseVectorSpecs(vectors) 151 | if err != nil { 152 | return err 153 | } 154 | 155 | for _, spec := range vectorSpecs { 156 | err := renderTemplates([]templateSpec{ 157 | {name: "vector", template: templates.VectorTemplate}, 158 | {name: "slice", template: templates.SliceTemplate}}, 159 | spec, buf) 160 | 161 | if err != nil { 162 | return err 163 | } 164 | } 165 | 166 | return nil 167 | } 168 | 169 | type vectorSpec struct { 170 | VectorTypeName string 171 | TypeName string 172 | } 173 | 174 | func parseVectorSpecs(vectorDescriptor string) ([]vectorSpec, error) { 175 | result := make([]vectorSpec, 0) 176 | descriptors := strings.Split(vectorDescriptor, ";") 177 | r := regexp.MustCompile(`([A-Za-z0-9]+)<(\*?[A-Za-z0-9.]+)>`) 178 | for _, d := range descriptors { 179 | m := r.FindStringSubmatch(strings.TrimSpace(d)) 180 | if len(m) != 3 { 181 | return nil, fmt.Errorf("Invalid vector specification: %s", d) 182 | } 183 | 184 | result = append(result, vectorSpec{VectorTypeName: m[1], TypeName: m[2]}) 185 | } 186 | 187 | return result, nil 188 | } 189 | 190 | /////////// 191 | /// Map /// 192 | /////////// 193 | 194 | func renderMaps(buf *bytes.Buffer, maps string) error { 195 | maps = removeWhiteSpaces(maps) 196 | if maps == "" { 197 | return nil 198 | } 199 | 200 | mapSpecs, err := parseMapSpecs(maps) 201 | if err != nil { 202 | return err 203 | } 204 | 205 | for _, spec := range mapSpecs { 206 | err := renderTemplates([]templateSpec{ 207 | {name: "map_template", template: templates.PrivateMapTemplate}, 208 | {name: "public_map_template", template: templates.PublicMapTemplate}}, 209 | spec, buf) 210 | 211 | if err != nil { 212 | return err 213 | } 214 | } 215 | 216 | return nil 217 | } 218 | 219 | type mapSpec struct { 220 | MapTypeName string 221 | MapItemTypeName string 222 | MapKeyTypeName string 223 | MapValueTypeName string 224 | MapKeyHashFunc string 225 | } 226 | 227 | func hashFunc(typ string) string { 228 | for _, hashTyp := range []string{"byte", "bool", "rune", "string", 229 | "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "int", "uint", "float32", "float64"} { 230 | if typ == hashTyp { 231 | return typ + "Hash" 232 | } 233 | } 234 | 235 | return "interfaceHash" 236 | } 237 | 238 | func parseMapSpecs(mapDescriptor string) ([]mapSpec, error) { 239 | result := make([]mapSpec, 0) 240 | descriptors := strings.Split(mapDescriptor, ";") 241 | r := regexp.MustCompile(`([A-Za-z0-9]+)<([A-Za-z0-9.]+),(\*?[A-Za-z0-9.]+)>`) 242 | for _, d := range descriptors { 243 | m := r.FindStringSubmatch(strings.TrimSpace(d)) 244 | if len(m) != 4 { 245 | return nil, fmt.Errorf("Invalid map specification: %s", d) 246 | } 247 | 248 | keyTypeName := m[2] 249 | result = append(result, 250 | mapSpec{MapTypeName: m[1], MapItemTypeName: m[1] + "Item", MapKeyTypeName: keyTypeName, MapValueTypeName: m[3], MapKeyHashFunc: hashFunc(keyTypeName)}) 251 | } 252 | 253 | return result, nil 254 | } 255 | 256 | /////////// 257 | /// Set /// 258 | /////////// 259 | 260 | func renderSet(buf *bytes.Buffer, sets string) error { 261 | sets = strings.Join(strings.Fields(sets), "") 262 | if sets == "" { 263 | return nil 264 | } 265 | 266 | setSpecs, err := parseSetSpecs(sets) 267 | if err != nil { 268 | return err 269 | } 270 | 271 | for _, spec := range setSpecs { 272 | err := renderTemplates([]templateSpec{ 273 | {name: "private_map_template", template: templates.PrivateMapTemplate}, 274 | {name: "set_template", template: templates.SetTemplate}}, 275 | spec, buf) 276 | 277 | if err != nil { 278 | return err 279 | } 280 | } 281 | 282 | return err 283 | } 284 | 285 | type setSpec struct { 286 | mapSpec 287 | SetTypeName string 288 | } 289 | 290 | func parseSetSpecs(setDescriptor string) ([]setSpec, error) { 291 | result := make([]setSpec, 0) 292 | descriptors := strings.Split(setDescriptor, ";") 293 | r := regexp.MustCompile(`([A-Za-z0-9]+)<(\*?[A-Za-z0-9.]+)>`) 294 | for _, d := range descriptors { 295 | m := r.FindStringSubmatch(strings.TrimSpace(d)) 296 | if len(m) != 3 { 297 | return nil, fmt.Errorf("Invalid set specification: %s", d) 298 | } 299 | 300 | keyTypeName := m[2] 301 | mapName := "private" + m[1] + "Map" 302 | result = append(result, 303 | setSpec{ 304 | mapSpec: mapSpec{MapTypeName: mapName, MapItemTypeName: mapName + "Item", MapKeyTypeName: keyTypeName, MapValueTypeName: "struct{}", MapKeyHashFunc: hashFunc(keyTypeName)}, 305 | SetTypeName: m[1]}) 306 | } 307 | 308 | return result, nil 309 | } 310 | 311 | //////////// 312 | /// File /// 313 | //////////// 314 | 315 | func writeFile(buf *bytes.Buffer, file string) error { 316 | if file == "" { 317 | return errors.New("Output file must be specified") 318 | } 319 | 320 | f, err := os.Create(file) 321 | if err != nil { 322 | return err 323 | } 324 | defer f.Close() 325 | 326 | // The equivalent of "go fmt" before writing content 327 | src := buf.Bytes() 328 | fmtSrc, err := format.Source(src) 329 | if err != nil { 330 | os.Stdout.WriteString(string(src)) 331 | return errors.Wrap(err, "Format error") 332 | } 333 | 334 | _, err = f.Write(fmtSrc) 335 | return err 336 | } 337 | -------------------------------------------------------------------------------- /examples/collections.go: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "hash/crc32" 7 | "math" 8 | ) 9 | 10 | const shiftSize = 5 11 | const nodeSize = 32 12 | const shiftBitMask = 0x1F 13 | 14 | type commonNode interface{} 15 | 16 | var emptyCommonNode commonNode = []commonNode{} 17 | 18 | func uintMin(a, b uint) uint { 19 | if a < b { 20 | return a 21 | } 22 | 23 | return b 24 | } 25 | 26 | func newPath(shift uint, node commonNode) commonNode { 27 | if shift == 0 { 28 | return node 29 | } 30 | 31 | return newPath(shift-shiftSize, commonNode([]commonNode{node})) 32 | } 33 | 34 | func assertSliceOk(start, stop, len int) { 35 | if start < 0 { 36 | panic(fmt.Sprintf("Invalid slice index %d (index must be non-negative)", start)) 37 | } 38 | 39 | if start > stop { 40 | panic(fmt.Sprintf("Invalid slice index: %d > %d", start, stop)) 41 | } 42 | 43 | if stop > len { 44 | panic(fmt.Sprintf("Slice bounds out of range, start=%d, stop=%d, len=%d", start, stop, len)) 45 | } 46 | } 47 | 48 | const upperMapLoadFactor float64 = 8.0 49 | const lowerMapLoadFactor float64 = 2.0 50 | const initialMapLoadFactor float64 = (upperMapLoadFactor + lowerMapLoadFactor) / 2 51 | 52 | ////////////////////////// 53 | //// Hash functions ////// 54 | ////////////////////////// 55 | 56 | func hash(x []byte) uint32 { 57 | return crc32.ChecksumIEEE(x) 58 | } 59 | 60 | func interfaceHash(x interface{}) uint32 { 61 | return hash([]byte(fmt.Sprintf("%v", x))) 62 | } 63 | 64 | func byteHash(x byte) uint32 { 65 | return hash([]byte{x}) 66 | } 67 | 68 | func uint8Hash(x uint8) uint32 { 69 | return byteHash(byte(x)) 70 | } 71 | 72 | func int8Hash(x int8) uint32 { 73 | return uint8Hash(uint8(x)) 74 | } 75 | 76 | func uint16Hash(x uint16) uint32 { 77 | bX := make([]byte, 2) 78 | binary.LittleEndian.PutUint16(bX, x) 79 | return hash(bX) 80 | } 81 | 82 | func int16Hash(x int16) uint32 { 83 | return uint16Hash(uint16(x)) 84 | } 85 | 86 | func uint32Hash(x uint32) uint32 { 87 | bX := make([]byte, 4) 88 | binary.LittleEndian.PutUint32(bX, x) 89 | return hash(bX) 90 | } 91 | 92 | func int32Hash(x int32) uint32 { 93 | return uint32Hash(uint32(x)) 94 | } 95 | 96 | func uint64Hash(x uint64) uint32 { 97 | bX := make([]byte, 8) 98 | binary.LittleEndian.PutUint64(bX, x) 99 | return hash(bX) 100 | } 101 | 102 | func int64Hash(x int64) uint32 { 103 | return uint64Hash(uint64(x)) 104 | } 105 | 106 | func intHash(x int) uint32 { 107 | return int64Hash(int64(x)) 108 | } 109 | 110 | func uintHash(x uint) uint32 { 111 | return uint64Hash(uint64(x)) 112 | } 113 | 114 | func boolHash(x bool) uint32 { 115 | if x { 116 | return 1 117 | } 118 | 119 | return 0 120 | } 121 | 122 | func runeHash(x rune) uint32 { 123 | return int32Hash(int32(x)) 124 | } 125 | 126 | func stringHash(x string) uint32 { 127 | return hash([]byte(x)) 128 | } 129 | 130 | func float64Hash(x float64) uint32 { 131 | return uint64Hash(math.Float64bits(x)) 132 | } 133 | 134 | func float32Hash(x float32) uint32 { 135 | return uint32Hash(math.Float32bits(x)) 136 | } 137 | 138 | ////////////// 139 | /// Vector /// 140 | ////////////// 141 | 142 | // A IntVector is an ordered persistent/immutable collection of items corresponding roughly 143 | // to the use cases for a slice. 144 | type IntVector struct { 145 | tail []int 146 | root commonNode 147 | len uint 148 | shift uint 149 | } 150 | 151 | var emptyIntVectorTail = make([]int, 0) 152 | var emptyIntVector *IntVector = &IntVector{root: emptyCommonNode, shift: shiftSize, tail: emptyIntVectorTail} 153 | 154 | // NewIntVector returns a new IntVector containing the items provided in items. 155 | func NewIntVector(items ...int) *IntVector { 156 | return emptyIntVector.Append(items...) 157 | } 158 | 159 | // Get returns the element at position i. 160 | func (v *IntVector) Get(i int) int { 161 | if i < 0 || uint(i) >= v.len { 162 | panic("Index out of bounds") 163 | } 164 | 165 | return v.sliceFor(uint(i))[i&shiftBitMask] 166 | } 167 | 168 | func (v *IntVector) sliceFor(i uint) []int { 169 | if i >= v.tailOffset() { 170 | return v.tail 171 | } 172 | 173 | node := v.root 174 | for level := v.shift; level > 0; level -= shiftSize { 175 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 176 | } 177 | 178 | return node.([]int) 179 | } 180 | 181 | func (v *IntVector) tailOffset() uint { 182 | if v.len < nodeSize { 183 | return 0 184 | } 185 | 186 | return ((v.len - 1) >> shiftSize) << shiftSize 187 | } 188 | 189 | // Set returns a new vector with the element at position i set to item. 190 | func (v *IntVector) Set(i int, item int) *IntVector { 191 | if i < 0 || uint(i) >= v.len { 192 | panic("Index out of bounds") 193 | } 194 | 195 | if uint(i) >= v.tailOffset() { 196 | newTail := make([]int, len(v.tail)) 197 | copy(newTail, v.tail) 198 | newTail[i&shiftBitMask] = item 199 | return &IntVector{root: v.root, tail: newTail, len: v.len, shift: v.shift} 200 | } 201 | 202 | return &IntVector{root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 203 | } 204 | 205 | func (v *IntVector) doAssoc(level uint, node commonNode, i uint, item int) commonNode { 206 | if level == 0 { 207 | ret := make([]int, nodeSize) 208 | copy(ret, node.([]int)) 209 | ret[i&shiftBitMask] = item 210 | return ret 211 | } 212 | 213 | ret := make([]commonNode, nodeSize) 214 | copy(ret, node.([]commonNode)) 215 | subidx := (i >> level) & shiftBitMask 216 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 217 | return ret 218 | } 219 | 220 | func (v *IntVector) pushTail(level uint, parent commonNode, tailNode []int) commonNode { 221 | subIdx := ((v.len - 1) >> level) & shiftBitMask 222 | parentNode := parent.([]commonNode) 223 | ret := make([]commonNode, subIdx+1) 224 | copy(ret, parentNode) 225 | var nodeToInsert commonNode 226 | 227 | if level == shiftSize { 228 | nodeToInsert = tailNode 229 | } else if subIdx < uint(len(parentNode)) { 230 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 231 | } else { 232 | nodeToInsert = newPath(level-shiftSize, tailNode) 233 | } 234 | 235 | ret[subIdx] = nodeToInsert 236 | return ret 237 | } 238 | 239 | // Append returns a new vector with item(s) appended to it. 240 | func (v *IntVector) Append(item ...int) *IntVector { 241 | result := v 242 | itemLen := uint(len(item)) 243 | for insertOffset := uint(0); insertOffset < itemLen; { 244 | tailLen := result.len - result.tailOffset() 245 | tailFree := nodeSize - tailLen 246 | if tailFree == 0 { 247 | result = result.pushLeafNode(result.tail) 248 | result.tail = emptyIntVector.tail 249 | tailFree = nodeSize 250 | tailLen = 0 251 | } 252 | 253 | batchLen := uintMin(itemLen-insertOffset, tailFree) 254 | newTail := make([]int, 0, tailLen+batchLen) 255 | newTail = append(newTail, result.tail...) 256 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 257 | result = &IntVector{root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 258 | insertOffset += batchLen 259 | } 260 | 261 | return result 262 | } 263 | 264 | func (v *IntVector) pushLeafNode(node []int) *IntVector { 265 | var newRoot commonNode 266 | newShift := v.shift 267 | 268 | // Root overflow? 269 | if (v.len >> shiftSize) > (1 << v.shift) { 270 | newNode := newPath(v.shift, node) 271 | newRoot = commonNode([]commonNode{v.root, newNode}) 272 | newShift = v.shift + shiftSize 273 | } else { 274 | newRoot = v.pushTail(v.shift, v.root, node) 275 | } 276 | 277 | return &IntVector{root: newRoot, tail: v.tail, len: v.len, shift: newShift} 278 | } 279 | 280 | // Slice returns a IntVectorSlice that refers to all elements [start,stop) in v. 281 | func (v *IntVector) Slice(start, stop int) *IntVectorSlice { 282 | assertSliceOk(start, stop, v.Len()) 283 | return &IntVectorSlice{vector: v, start: start, stop: stop} 284 | } 285 | 286 | // Len returns the length of v. 287 | func (v *IntVector) Len() int { 288 | return int(v.len) 289 | } 290 | 291 | // Range calls f repeatedly passing it each element in v in order as argument until either 292 | // all elements have been visited or f returns false. 293 | func (v *IntVector) Range(f func(int) bool) { 294 | var currentNode []int 295 | for i := uint(0); i < v.len; i++ { 296 | if i&shiftBitMask == 0 { 297 | currentNode = v.sliceFor(i) 298 | } 299 | 300 | if !f(currentNode[i&shiftBitMask]) { 301 | return 302 | } 303 | } 304 | } 305 | 306 | // ToNativeSlice returns a Go slice containing all elements of v 307 | func (v *IntVector) ToNativeSlice() []int { 308 | result := make([]int, 0, v.len) 309 | for i := uint(0); i < v.len; i += nodeSize { 310 | result = append(result, v.sliceFor(i)...) 311 | } 312 | 313 | return result 314 | } 315 | 316 | //////////////// 317 | //// Slice ///// 318 | //////////////// 319 | 320 | // IntVectorSlice is a slice type backed by a IntVector. 321 | type IntVectorSlice struct { 322 | vector *IntVector 323 | start, stop int 324 | } 325 | 326 | // NewIntVectorSlice returns a new NewIntVectorSlice containing the items provided in items. 327 | func NewIntVectorSlice(items ...int) *IntVectorSlice { 328 | return &IntVectorSlice{vector: emptyIntVector.Append(items...), start: 0, stop: len(items)} 329 | } 330 | 331 | // Len returns the length of s. 332 | func (s *IntVectorSlice) Len() int { 333 | return s.stop - s.start 334 | } 335 | 336 | // Get returns the element at position i. 337 | func (s *IntVectorSlice) Get(i int) int { 338 | if i < 0 || s.start+i >= s.stop { 339 | panic("Index out of bounds") 340 | } 341 | 342 | return s.vector.Get(s.start + i) 343 | } 344 | 345 | // Set returns a new slice with the element at position i set to item. 346 | func (s *IntVectorSlice) Set(i int, item int) *IntVectorSlice { 347 | if i < 0 || s.start+i >= s.stop { 348 | panic("Index out of bounds") 349 | } 350 | 351 | return s.vector.Set(s.start+i, item).Slice(s.start, s.stop) 352 | } 353 | 354 | // Append returns a new slice with item(s) appended to it. 355 | func (s *IntVectorSlice) Append(items ...int) *IntVectorSlice { 356 | newSlice := IntVectorSlice{vector: s.vector, start: s.start, stop: s.stop + len(items)} 357 | 358 | // If this is v slice that has an upper bound that is lower than the backing 359 | // vector then set the values in the backing vector to achieve some structural 360 | // sharing. 361 | itemPos := 0 362 | for ; s.stop+itemPos < s.vector.Len() && itemPos < len(items); itemPos++ { 363 | newSlice.vector = newSlice.vector.Set(s.stop+itemPos, items[itemPos]) 364 | } 365 | 366 | // For the rest just append it to the underlying vector 367 | newSlice.vector = newSlice.vector.Append(items[itemPos:]...) 368 | return &newSlice 369 | } 370 | 371 | // Slice returns a IntVectorSlice that refers to all elements [start,stop) in s. 372 | func (s *IntVectorSlice) Slice(start, stop int) *IntVectorSlice { 373 | assertSliceOk(start, stop, s.stop-s.start) 374 | return &IntVectorSlice{vector: s.vector, start: s.start + start, stop: s.start + stop} 375 | } 376 | 377 | // Range calls f repeatedly passing it each element in s in order as argument until either 378 | // all elements have been visited or f returns false. 379 | func (s *IntVectorSlice) Range(f func(int) bool) { 380 | var currentNode []int 381 | for i := uint(s.start); i < uint(s.stop); i++ { 382 | if i&shiftBitMask == 0 || i == uint(s.start) { 383 | currentNode = s.vector.sliceFor(uint(i)) 384 | } 385 | 386 | if !f(currentNode[i&shiftBitMask]) { 387 | return 388 | } 389 | } 390 | } 391 | 392 | /////////// 393 | /// Map /// 394 | /////////// 395 | 396 | ////////////////////// 397 | /// Backing vector /// 398 | ////////////////////// 399 | 400 | type privatePersonBySsnItemBucketVector struct { 401 | tail []privatePersonBySsnItemBucket 402 | root commonNode 403 | len uint 404 | shift uint 405 | } 406 | 407 | type PersonBySsnItem struct { 408 | Key string 409 | Value Person 410 | } 411 | 412 | type privatePersonBySsnItemBucket []PersonBySsnItem 413 | 414 | var emptyPersonBySsnItemBucketVectorTail = make([]privatePersonBySsnItemBucket, 0) 415 | var emptyPersonBySsnItemBucketVector *privatePersonBySsnItemBucketVector = &privatePersonBySsnItemBucketVector{root: emptyCommonNode, shift: shiftSize, tail: emptyPersonBySsnItemBucketVectorTail} 416 | 417 | func (v *privatePersonBySsnItemBucketVector) Get(i int) privatePersonBySsnItemBucket { 418 | if i < 0 || uint(i) >= v.len { 419 | panic("Index out of bounds") 420 | } 421 | 422 | return v.sliceFor(uint(i))[i&shiftBitMask] 423 | } 424 | 425 | func (v *privatePersonBySsnItemBucketVector) sliceFor(i uint) []privatePersonBySsnItemBucket { 426 | if i >= v.tailOffset() { 427 | return v.tail 428 | } 429 | 430 | node := v.root 431 | for level := v.shift; level > 0; level -= shiftSize { 432 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 433 | } 434 | 435 | return node.([]privatePersonBySsnItemBucket) 436 | } 437 | 438 | func (v *privatePersonBySsnItemBucketVector) tailOffset() uint { 439 | if v.len < nodeSize { 440 | return 0 441 | } 442 | 443 | return ((v.len - 1) >> shiftSize) << shiftSize 444 | } 445 | 446 | func (v *privatePersonBySsnItemBucketVector) Set(i int, item privatePersonBySsnItemBucket) *privatePersonBySsnItemBucketVector { 447 | if i < 0 || uint(i) >= v.len { 448 | panic("Index out of bounds") 449 | } 450 | 451 | if uint(i) >= v.tailOffset() { 452 | newTail := make([]privatePersonBySsnItemBucket, len(v.tail)) 453 | copy(newTail, v.tail) 454 | newTail[i&shiftBitMask] = item 455 | return &privatePersonBySsnItemBucketVector{root: v.root, tail: newTail, len: v.len, shift: v.shift} 456 | } 457 | 458 | return &privatePersonBySsnItemBucketVector{root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 459 | } 460 | 461 | func (v *privatePersonBySsnItemBucketVector) doAssoc(level uint, node commonNode, i uint, item privatePersonBySsnItemBucket) commonNode { 462 | if level == 0 { 463 | ret := make([]privatePersonBySsnItemBucket, nodeSize) 464 | copy(ret, node.([]privatePersonBySsnItemBucket)) 465 | ret[i&shiftBitMask] = item 466 | return ret 467 | } 468 | 469 | ret := make([]commonNode, nodeSize) 470 | copy(ret, node.([]commonNode)) 471 | subidx := (i >> level) & shiftBitMask 472 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 473 | return ret 474 | } 475 | 476 | func (v *privatePersonBySsnItemBucketVector) pushTail(level uint, parent commonNode, tailNode []privatePersonBySsnItemBucket) commonNode { 477 | subIdx := ((v.len - 1) >> level) & shiftBitMask 478 | parentNode := parent.([]commonNode) 479 | ret := make([]commonNode, subIdx+1) 480 | copy(ret, parentNode) 481 | var nodeToInsert commonNode 482 | 483 | if level == shiftSize { 484 | nodeToInsert = tailNode 485 | } else if subIdx < uint(len(parentNode)) { 486 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 487 | } else { 488 | nodeToInsert = newPath(level-shiftSize, tailNode) 489 | } 490 | 491 | ret[subIdx] = nodeToInsert 492 | return ret 493 | } 494 | 495 | func (v *privatePersonBySsnItemBucketVector) Append(item ...privatePersonBySsnItemBucket) *privatePersonBySsnItemBucketVector { 496 | result := v 497 | itemLen := uint(len(item)) 498 | for insertOffset := uint(0); insertOffset < itemLen; { 499 | tailLen := result.len - result.tailOffset() 500 | tailFree := nodeSize - tailLen 501 | if tailFree == 0 { 502 | result = result.pushLeafNode(result.tail) 503 | result.tail = emptyPersonBySsnItemBucketVector.tail 504 | tailFree = nodeSize 505 | tailLen = 0 506 | } 507 | 508 | batchLen := uintMin(itemLen-insertOffset, tailFree) 509 | newTail := make([]privatePersonBySsnItemBucket, 0, tailLen+batchLen) 510 | newTail = append(newTail, result.tail...) 511 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 512 | result = &privatePersonBySsnItemBucketVector{root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 513 | insertOffset += batchLen 514 | } 515 | 516 | return result 517 | } 518 | 519 | func (v *privatePersonBySsnItemBucketVector) pushLeafNode(node []privatePersonBySsnItemBucket) *privatePersonBySsnItemBucketVector { 520 | var newRoot commonNode 521 | newShift := v.shift 522 | 523 | // Root overflow? 524 | if (v.len >> shiftSize) > (1 << v.shift) { 525 | newNode := newPath(v.shift, node) 526 | newRoot = commonNode([]commonNode{v.root, newNode}) 527 | newShift = v.shift + shiftSize 528 | } else { 529 | newRoot = v.pushTail(v.shift, v.root, node) 530 | } 531 | 532 | return &privatePersonBySsnItemBucketVector{root: newRoot, tail: v.tail, len: v.len, shift: newShift} 533 | } 534 | 535 | func (v *privatePersonBySsnItemBucketVector) Len() int { 536 | return int(v.len) 537 | } 538 | 539 | func (v *privatePersonBySsnItemBucketVector) Range(f func(privatePersonBySsnItemBucket) bool) { 540 | var currentNode []privatePersonBySsnItemBucket 541 | for i := uint(0); i < v.len; i++ { 542 | if i&shiftBitMask == 0 { 543 | currentNode = v.sliceFor(uint(i)) 544 | } 545 | 546 | if !f(currentNode[i&shiftBitMask]) { 547 | return 548 | } 549 | } 550 | } 551 | 552 | // PersonBySsn is a persistent key - value map 553 | type PersonBySsn struct { 554 | backingVector *privatePersonBySsnItemBucketVector 555 | len int 556 | } 557 | 558 | func (m *PersonBySsn) pos(key string) int { 559 | return int(uint64(stringHash(key)) % uint64(m.backingVector.Len())) 560 | } 561 | 562 | // Helper type used during map creation and reallocation 563 | type privatePersonBySsnItemBuckets struct { 564 | buckets []privatePersonBySsnItemBucket 565 | length int 566 | } 567 | 568 | func newPrivatePersonBySsnItemBuckets(itemCount int) *privatePersonBySsnItemBuckets { 569 | size := int(float64(itemCount)/initialMapLoadFactor) + 1 570 | buckets := make([]privatePersonBySsnItemBucket, size) 571 | return &privatePersonBySsnItemBuckets{buckets: buckets} 572 | } 573 | 574 | func (b *privatePersonBySsnItemBuckets) AddItem(item PersonBySsnItem) { 575 | ix := int(uint64(stringHash(item.Key)) % uint64(len(b.buckets))) 576 | bucket := b.buckets[ix] 577 | if bucket != nil { 578 | // Hash collision, merge with existing bucket 579 | for keyIx, bItem := range bucket { 580 | if item.Key == bItem.Key { 581 | bucket[keyIx] = item 582 | return 583 | } 584 | } 585 | 586 | b.buckets[ix] = append(bucket, PersonBySsnItem{Key: item.Key, Value: item.Value}) 587 | b.length++ 588 | } else { 589 | bucket := make(privatePersonBySsnItemBucket, 0, int(math.Max(initialMapLoadFactor, 1.0))) 590 | b.buckets[ix] = append(bucket, item) 591 | b.length++ 592 | } 593 | } 594 | 595 | func (b *privatePersonBySsnItemBuckets) AddItemsFromMap(m *PersonBySsn) { 596 | m.backingVector.Range(func(bucket privatePersonBySsnItemBucket) bool { 597 | for _, item := range bucket { 598 | b.AddItem(item) 599 | } 600 | return true 601 | }) 602 | } 603 | 604 | func newPersonBySsn(items []PersonBySsnItem) *PersonBySsn { 605 | buckets := newPrivatePersonBySsnItemBuckets(len(items)) 606 | for _, item := range items { 607 | buckets.AddItem(item) 608 | } 609 | 610 | return &PersonBySsn{backingVector: emptyPersonBySsnItemBucketVector.Append(buckets.buckets...), len: buckets.length} 611 | } 612 | 613 | // Len returns the number of items in m. 614 | func (m *PersonBySsn) Len() int { 615 | return int(m.len) 616 | } 617 | 618 | // Load returns value identified by key. ok is set to true if key exists in the map, false otherwise. 619 | func (m *PersonBySsn) Load(key string) (value Person, ok bool) { 620 | bucket := m.backingVector.Get(m.pos(key)) 621 | if bucket != nil { 622 | for _, item := range bucket { 623 | if item.Key == key { 624 | return item.Value, true 625 | } 626 | } 627 | } 628 | 629 | var zeroValue Person 630 | return zeroValue, false 631 | } 632 | 633 | // Store returns a new PersonBySsn containing value identified by key. 634 | func (m *PersonBySsn) Store(key string, value Person) *PersonBySsn { 635 | // Grow backing vector if load factor is too high 636 | if m.Len() >= m.backingVector.Len()*int(upperMapLoadFactor) { 637 | buckets := newPrivatePersonBySsnItemBuckets(m.Len() + 1) 638 | buckets.AddItemsFromMap(m) 639 | buckets.AddItem(PersonBySsnItem{Key: key, Value: value}) 640 | return &PersonBySsn{backingVector: emptyPersonBySsnItemBucketVector.Append(buckets.buckets...), len: buckets.length} 641 | } 642 | 643 | pos := m.pos(key) 644 | bucket := m.backingVector.Get(pos) 645 | if bucket != nil { 646 | for ix, item := range bucket { 647 | if item.Key == key { 648 | // Overwrite existing item 649 | newBucket := make(privatePersonBySsnItemBucket, len(bucket)) 650 | copy(newBucket, bucket) 651 | newBucket[ix] = PersonBySsnItem{Key: key, Value: value} 652 | return &PersonBySsn{backingVector: m.backingVector.Set(pos, newBucket), len: m.len} 653 | } 654 | } 655 | 656 | // Add new item to bucket 657 | newBucket := make(privatePersonBySsnItemBucket, len(bucket), len(bucket)+1) 658 | copy(newBucket, bucket) 659 | newBucket = append(newBucket, PersonBySsnItem{Key: key, Value: value}) 660 | return &PersonBySsn{backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 661 | } 662 | 663 | item := PersonBySsnItem{Key: key, Value: value} 664 | newBucket := privatePersonBySsnItemBucket{item} 665 | return &PersonBySsn{backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 666 | } 667 | 668 | // Delete returns a new PersonBySsn without the element identified by key. 669 | func (m *PersonBySsn) Delete(key string) *PersonBySsn { 670 | pos := m.pos(key) 671 | bucket := m.backingVector.Get(pos) 672 | if bucket != nil { 673 | newBucket := make(privatePersonBySsnItemBucket, 0) 674 | for _, item := range bucket { 675 | if item.Key != key { 676 | newBucket = append(newBucket, item) 677 | } 678 | } 679 | 680 | removedItemCount := len(bucket) - len(newBucket) 681 | if removedItemCount == 0 { 682 | return m 683 | } 684 | 685 | if len(newBucket) == 0 { 686 | newBucket = nil 687 | } 688 | 689 | newMap := &PersonBySsn{backingVector: m.backingVector.Set(pos, newBucket), len: m.len - removedItemCount} 690 | if newMap.backingVector.Len() > 1 && newMap.Len() < newMap.backingVector.Len()*int(lowerMapLoadFactor) { 691 | // Shrink backing vector if needed to avoid occupying excessive space 692 | buckets := newPrivatePersonBySsnItemBuckets(newMap.Len()) 693 | buckets.AddItemsFromMap(newMap) 694 | return &PersonBySsn{backingVector: emptyPersonBySsnItemBucketVector.Append(buckets.buckets...), len: buckets.length} 695 | } 696 | 697 | return newMap 698 | } 699 | 700 | return m 701 | } 702 | 703 | // Range calls f repeatedly passing it each key and value as argument until either 704 | // all elements have been visited or f returns false. 705 | func (m *PersonBySsn) Range(f func(string, Person) bool) { 706 | m.backingVector.Range(func(bucket privatePersonBySsnItemBucket) bool { 707 | for _, item := range bucket { 708 | if !f(item.Key, item.Value) { 709 | return false 710 | } 711 | } 712 | return true 713 | }) 714 | } 715 | 716 | // ToNativeMap returns a native Go map containing all elements of m. 717 | func (m *PersonBySsn) ToNativeMap() map[string]Person { 718 | result := make(map[string]Person) 719 | m.Range(func(key string, value Person) bool { 720 | result[key] = value 721 | return true 722 | }) 723 | 724 | return result 725 | } 726 | 727 | //////////////////// 728 | /// Constructors /// 729 | //////////////////// 730 | 731 | // NewPersonBySsn returns a new PersonBySsn containing all items in items. 732 | func NewPersonBySsn(items ...PersonBySsnItem) *PersonBySsn { 733 | return newPersonBySsn(items) 734 | } 735 | 736 | // NewPersonBySsnFromNativeMap returns a new PersonBySsn containing all items in m. 737 | func NewPersonBySsnFromNativeMap(m map[string]Person) *PersonBySsn { 738 | buckets := newPrivatePersonBySsnItemBuckets(len(m)) 739 | for key, value := range m { 740 | buckets.AddItem(PersonBySsnItem{Key: key, Value: value}) 741 | } 742 | 743 | return &PersonBySsn{backingVector: emptyPersonBySsnItemBucketVector.Append(buckets.buckets...), len: buckets.length} 744 | } 745 | 746 | /////////// 747 | /// Map /// 748 | /////////// 749 | 750 | ////////////////////// 751 | /// Backing vector /// 752 | ////////////////////// 753 | 754 | type privateprivatePersonsMapItemBucketVector struct { 755 | tail []privateprivatePersonsMapItemBucket 756 | root commonNode 757 | len uint 758 | shift uint 759 | } 760 | 761 | type privatePersonsMapItem struct { 762 | Key Person 763 | Value struct{} 764 | } 765 | 766 | type privateprivatePersonsMapItemBucket []privatePersonsMapItem 767 | 768 | var emptyprivatePersonsMapItemBucketVectorTail = make([]privateprivatePersonsMapItemBucket, 0) 769 | var emptyprivatePersonsMapItemBucketVector *privateprivatePersonsMapItemBucketVector = &privateprivatePersonsMapItemBucketVector{root: emptyCommonNode, shift: shiftSize, tail: emptyprivatePersonsMapItemBucketVectorTail} 770 | 771 | func (v *privateprivatePersonsMapItemBucketVector) Get(i int) privateprivatePersonsMapItemBucket { 772 | if i < 0 || uint(i) >= v.len { 773 | panic("Index out of bounds") 774 | } 775 | 776 | return v.sliceFor(uint(i))[i&shiftBitMask] 777 | } 778 | 779 | func (v *privateprivatePersonsMapItemBucketVector) sliceFor(i uint) []privateprivatePersonsMapItemBucket { 780 | if i >= v.tailOffset() { 781 | return v.tail 782 | } 783 | 784 | node := v.root 785 | for level := v.shift; level > 0; level -= shiftSize { 786 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 787 | } 788 | 789 | return node.([]privateprivatePersonsMapItemBucket) 790 | } 791 | 792 | func (v *privateprivatePersonsMapItemBucketVector) tailOffset() uint { 793 | if v.len < nodeSize { 794 | return 0 795 | } 796 | 797 | return ((v.len - 1) >> shiftSize) << shiftSize 798 | } 799 | 800 | func (v *privateprivatePersonsMapItemBucketVector) Set(i int, item privateprivatePersonsMapItemBucket) *privateprivatePersonsMapItemBucketVector { 801 | if i < 0 || uint(i) >= v.len { 802 | panic("Index out of bounds") 803 | } 804 | 805 | if uint(i) >= v.tailOffset() { 806 | newTail := make([]privateprivatePersonsMapItemBucket, len(v.tail)) 807 | copy(newTail, v.tail) 808 | newTail[i&shiftBitMask] = item 809 | return &privateprivatePersonsMapItemBucketVector{root: v.root, tail: newTail, len: v.len, shift: v.shift} 810 | } 811 | 812 | return &privateprivatePersonsMapItemBucketVector{root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 813 | } 814 | 815 | func (v *privateprivatePersonsMapItemBucketVector) doAssoc(level uint, node commonNode, i uint, item privateprivatePersonsMapItemBucket) commonNode { 816 | if level == 0 { 817 | ret := make([]privateprivatePersonsMapItemBucket, nodeSize) 818 | copy(ret, node.([]privateprivatePersonsMapItemBucket)) 819 | ret[i&shiftBitMask] = item 820 | return ret 821 | } 822 | 823 | ret := make([]commonNode, nodeSize) 824 | copy(ret, node.([]commonNode)) 825 | subidx := (i >> level) & shiftBitMask 826 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 827 | return ret 828 | } 829 | 830 | func (v *privateprivatePersonsMapItemBucketVector) pushTail(level uint, parent commonNode, tailNode []privateprivatePersonsMapItemBucket) commonNode { 831 | subIdx := ((v.len - 1) >> level) & shiftBitMask 832 | parentNode := parent.([]commonNode) 833 | ret := make([]commonNode, subIdx+1) 834 | copy(ret, parentNode) 835 | var nodeToInsert commonNode 836 | 837 | if level == shiftSize { 838 | nodeToInsert = tailNode 839 | } else if subIdx < uint(len(parentNode)) { 840 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 841 | } else { 842 | nodeToInsert = newPath(level-shiftSize, tailNode) 843 | } 844 | 845 | ret[subIdx] = nodeToInsert 846 | return ret 847 | } 848 | 849 | func (v *privateprivatePersonsMapItemBucketVector) Append(item ...privateprivatePersonsMapItemBucket) *privateprivatePersonsMapItemBucketVector { 850 | result := v 851 | itemLen := uint(len(item)) 852 | for insertOffset := uint(0); insertOffset < itemLen; { 853 | tailLen := result.len - result.tailOffset() 854 | tailFree := nodeSize - tailLen 855 | if tailFree == 0 { 856 | result = result.pushLeafNode(result.tail) 857 | result.tail = emptyprivatePersonsMapItemBucketVector.tail 858 | tailFree = nodeSize 859 | tailLen = 0 860 | } 861 | 862 | batchLen := uintMin(itemLen-insertOffset, tailFree) 863 | newTail := make([]privateprivatePersonsMapItemBucket, 0, tailLen+batchLen) 864 | newTail = append(newTail, result.tail...) 865 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 866 | result = &privateprivatePersonsMapItemBucketVector{root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 867 | insertOffset += batchLen 868 | } 869 | 870 | return result 871 | } 872 | 873 | func (v *privateprivatePersonsMapItemBucketVector) pushLeafNode(node []privateprivatePersonsMapItemBucket) *privateprivatePersonsMapItemBucketVector { 874 | var newRoot commonNode 875 | newShift := v.shift 876 | 877 | // Root overflow? 878 | if (v.len >> shiftSize) > (1 << v.shift) { 879 | newNode := newPath(v.shift, node) 880 | newRoot = commonNode([]commonNode{v.root, newNode}) 881 | newShift = v.shift + shiftSize 882 | } else { 883 | newRoot = v.pushTail(v.shift, v.root, node) 884 | } 885 | 886 | return &privateprivatePersonsMapItemBucketVector{root: newRoot, tail: v.tail, len: v.len, shift: newShift} 887 | } 888 | 889 | func (v *privateprivatePersonsMapItemBucketVector) Len() int { 890 | return int(v.len) 891 | } 892 | 893 | func (v *privateprivatePersonsMapItemBucketVector) Range(f func(privateprivatePersonsMapItemBucket) bool) { 894 | var currentNode []privateprivatePersonsMapItemBucket 895 | for i := uint(0); i < v.len; i++ { 896 | if i&shiftBitMask == 0 { 897 | currentNode = v.sliceFor(uint(i)) 898 | } 899 | 900 | if !f(currentNode[i&shiftBitMask]) { 901 | return 902 | } 903 | } 904 | } 905 | 906 | // privatePersonsMap is a persistent key - value map 907 | type privatePersonsMap struct { 908 | backingVector *privateprivatePersonsMapItemBucketVector 909 | len int 910 | } 911 | 912 | func (m *privatePersonsMap) pos(key Person) int { 913 | return int(uint64(interfaceHash(key)) % uint64(m.backingVector.Len())) 914 | } 915 | 916 | // Helper type used during map creation and reallocation 917 | type privateprivatePersonsMapItemBuckets struct { 918 | buckets []privateprivatePersonsMapItemBucket 919 | length int 920 | } 921 | 922 | func newPrivateprivatePersonsMapItemBuckets(itemCount int) *privateprivatePersonsMapItemBuckets { 923 | size := int(float64(itemCount)/initialMapLoadFactor) + 1 924 | buckets := make([]privateprivatePersonsMapItemBucket, size) 925 | return &privateprivatePersonsMapItemBuckets{buckets: buckets} 926 | } 927 | 928 | func (b *privateprivatePersonsMapItemBuckets) AddItem(item privatePersonsMapItem) { 929 | ix := int(uint64(interfaceHash(item.Key)) % uint64(len(b.buckets))) 930 | bucket := b.buckets[ix] 931 | if bucket != nil { 932 | // Hash collision, merge with existing bucket 933 | for keyIx, bItem := range bucket { 934 | if item.Key == bItem.Key { 935 | bucket[keyIx] = item 936 | return 937 | } 938 | } 939 | 940 | b.buckets[ix] = append(bucket, privatePersonsMapItem{Key: item.Key, Value: item.Value}) 941 | b.length++ 942 | } else { 943 | bucket := make(privateprivatePersonsMapItemBucket, 0, int(math.Max(initialMapLoadFactor, 1.0))) 944 | b.buckets[ix] = append(bucket, item) 945 | b.length++ 946 | } 947 | } 948 | 949 | func (b *privateprivatePersonsMapItemBuckets) AddItemsFromMap(m *privatePersonsMap) { 950 | m.backingVector.Range(func(bucket privateprivatePersonsMapItemBucket) bool { 951 | for _, item := range bucket { 952 | b.AddItem(item) 953 | } 954 | return true 955 | }) 956 | } 957 | 958 | func newprivatePersonsMap(items []privatePersonsMapItem) *privatePersonsMap { 959 | buckets := newPrivateprivatePersonsMapItemBuckets(len(items)) 960 | for _, item := range items { 961 | buckets.AddItem(item) 962 | } 963 | 964 | return &privatePersonsMap{backingVector: emptyprivatePersonsMapItemBucketVector.Append(buckets.buckets...), len: buckets.length} 965 | } 966 | 967 | // Len returns the number of items in m. 968 | func (m *privatePersonsMap) Len() int { 969 | return int(m.len) 970 | } 971 | 972 | // Load returns value identified by key. ok is set to true if key exists in the map, false otherwise. 973 | func (m *privatePersonsMap) Load(key Person) (value struct{}, ok bool) { 974 | bucket := m.backingVector.Get(m.pos(key)) 975 | if bucket != nil { 976 | for _, item := range bucket { 977 | if item.Key == key { 978 | return item.Value, true 979 | } 980 | } 981 | } 982 | 983 | var zeroValue struct{} 984 | return zeroValue, false 985 | } 986 | 987 | // Store returns a new privatePersonsMap containing value identified by key. 988 | func (m *privatePersonsMap) Store(key Person, value struct{}) *privatePersonsMap { 989 | // Grow backing vector if load factor is too high 990 | if m.Len() >= m.backingVector.Len()*int(upperMapLoadFactor) { 991 | buckets := newPrivateprivatePersonsMapItemBuckets(m.Len() + 1) 992 | buckets.AddItemsFromMap(m) 993 | buckets.AddItem(privatePersonsMapItem{Key: key, Value: value}) 994 | return &privatePersonsMap{backingVector: emptyprivatePersonsMapItemBucketVector.Append(buckets.buckets...), len: buckets.length} 995 | } 996 | 997 | pos := m.pos(key) 998 | bucket := m.backingVector.Get(pos) 999 | if bucket != nil { 1000 | for ix, item := range bucket { 1001 | if item.Key == key { 1002 | // Overwrite existing item 1003 | newBucket := make(privateprivatePersonsMapItemBucket, len(bucket)) 1004 | copy(newBucket, bucket) 1005 | newBucket[ix] = privatePersonsMapItem{Key: key, Value: value} 1006 | return &privatePersonsMap{backingVector: m.backingVector.Set(pos, newBucket), len: m.len} 1007 | } 1008 | } 1009 | 1010 | // Add new item to bucket 1011 | newBucket := make(privateprivatePersonsMapItemBucket, len(bucket), len(bucket)+1) 1012 | copy(newBucket, bucket) 1013 | newBucket = append(newBucket, privatePersonsMapItem{Key: key, Value: value}) 1014 | return &privatePersonsMap{backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 1015 | } 1016 | 1017 | item := privatePersonsMapItem{Key: key, Value: value} 1018 | newBucket := privateprivatePersonsMapItemBucket{item} 1019 | return &privatePersonsMap{backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 1020 | } 1021 | 1022 | // Delete returns a new privatePersonsMap without the element identified by key. 1023 | func (m *privatePersonsMap) Delete(key Person) *privatePersonsMap { 1024 | pos := m.pos(key) 1025 | bucket := m.backingVector.Get(pos) 1026 | if bucket != nil { 1027 | newBucket := make(privateprivatePersonsMapItemBucket, 0) 1028 | for _, item := range bucket { 1029 | if item.Key != key { 1030 | newBucket = append(newBucket, item) 1031 | } 1032 | } 1033 | 1034 | removedItemCount := len(bucket) - len(newBucket) 1035 | if removedItemCount == 0 { 1036 | return m 1037 | } 1038 | 1039 | if len(newBucket) == 0 { 1040 | newBucket = nil 1041 | } 1042 | 1043 | newMap := &privatePersonsMap{backingVector: m.backingVector.Set(pos, newBucket), len: m.len - removedItemCount} 1044 | if newMap.backingVector.Len() > 1 && newMap.Len() < newMap.backingVector.Len()*int(lowerMapLoadFactor) { 1045 | // Shrink backing vector if needed to avoid occupying excessive space 1046 | buckets := newPrivateprivatePersonsMapItemBuckets(newMap.Len()) 1047 | buckets.AddItemsFromMap(newMap) 1048 | return &privatePersonsMap{backingVector: emptyprivatePersonsMapItemBucketVector.Append(buckets.buckets...), len: buckets.length} 1049 | } 1050 | 1051 | return newMap 1052 | } 1053 | 1054 | return m 1055 | } 1056 | 1057 | // Range calls f repeatedly passing it each key and value as argument until either 1058 | // all elements have been visited or f returns false. 1059 | func (m *privatePersonsMap) Range(f func(Person, struct{}) bool) { 1060 | m.backingVector.Range(func(bucket privateprivatePersonsMapItemBucket) bool { 1061 | for _, item := range bucket { 1062 | if !f(item.Key, item.Value) { 1063 | return false 1064 | } 1065 | } 1066 | return true 1067 | }) 1068 | } 1069 | 1070 | // ToNativeMap returns a native Go map containing all elements of m. 1071 | func (m *privatePersonsMap) ToNativeMap() map[Person]struct{} { 1072 | result := make(map[Person]struct{}) 1073 | m.Range(func(key Person, value struct{}) bool { 1074 | result[key] = value 1075 | return true 1076 | }) 1077 | 1078 | return result 1079 | } 1080 | 1081 | // Persons is a persistent set 1082 | type Persons struct { 1083 | backingMap *privatePersonsMap 1084 | } 1085 | 1086 | // NewPersons returns a new Persons containing items. 1087 | func NewPersons(items ...Person) *Persons { 1088 | mapItems := make([]privatePersonsMapItem, 0, len(items)) 1089 | var mapValue struct{} 1090 | for _, x := range items { 1091 | mapItems = append(mapItems, privatePersonsMapItem{Key: x, Value: mapValue}) 1092 | } 1093 | 1094 | return &Persons{backingMap: newprivatePersonsMap(mapItems)} 1095 | } 1096 | 1097 | // Add returns a new Persons containing item. 1098 | func (s *Persons) Add(item Person) *Persons { 1099 | var mapValue struct{} 1100 | return &Persons{backingMap: s.backingMap.Store(item, mapValue)} 1101 | } 1102 | 1103 | // Delete returns a new Persons without item. 1104 | func (s *Persons) Delete(item Person) *Persons { 1105 | newMap := s.backingMap.Delete(item) 1106 | if newMap == s.backingMap { 1107 | return s 1108 | } 1109 | 1110 | return &Persons{backingMap: newMap} 1111 | } 1112 | 1113 | // Contains returns true if item is present in s, false otherwise. 1114 | func (s *Persons) Contains(item Person) bool { 1115 | _, ok := s.backingMap.Load(item) 1116 | return ok 1117 | } 1118 | 1119 | // Range calls f repeatedly passing it each element in s as argument until either 1120 | // all elements have been visited or f returns false. 1121 | func (s *Persons) Range(f func(Person) bool) { 1122 | s.backingMap.Range(func(k Person, _ struct{}) bool { 1123 | return f(k) 1124 | }) 1125 | } 1126 | 1127 | // IsSubset returns true if all elements in s are present in other, false otherwise. 1128 | func (s *Persons) IsSubset(other *Persons) bool { 1129 | if other.Len() < s.Len() { 1130 | return false 1131 | } 1132 | 1133 | isSubset := true 1134 | s.Range(func(item Person) bool { 1135 | if !other.Contains(item) { 1136 | isSubset = false 1137 | } 1138 | 1139 | return isSubset 1140 | }) 1141 | 1142 | return isSubset 1143 | } 1144 | 1145 | // IsSuperset returns true if all elements in other are present in s, false otherwise. 1146 | func (s *Persons) IsSuperset(other *Persons) bool { 1147 | return other.IsSubset(s) 1148 | } 1149 | 1150 | // Union returns a new Persons containing all elements present 1151 | // in either s or other. 1152 | func (s *Persons) Union(other *Persons) *Persons { 1153 | result := s 1154 | 1155 | // Simplest possible solution right now. Would probable be more efficient 1156 | // to concatenate two slices of elements from the two sets and create a 1157 | // new set from that slice for many cases. 1158 | other.Range(func(item Person) bool { 1159 | result = result.Add(item) 1160 | return true 1161 | }) 1162 | 1163 | return result 1164 | } 1165 | 1166 | // Equals returns true if s and other contains the same elements, false otherwise. 1167 | func (s *Persons) Equals(other *Persons) bool { 1168 | return s.Len() == other.Len() && s.IsSubset(other) 1169 | } 1170 | 1171 | func (s *Persons) difference(other *Persons) []Person { 1172 | items := make([]Person, 0) 1173 | s.Range(func(item Person) bool { 1174 | if !other.Contains(item) { 1175 | items = append(items, item) 1176 | } 1177 | 1178 | return true 1179 | }) 1180 | 1181 | return items 1182 | } 1183 | 1184 | // Difference returns a new Persons containing all elements present 1185 | // in s but not in other. 1186 | func (s *Persons) Difference(other *Persons) *Persons { 1187 | return NewPersons(s.difference(other)...) 1188 | } 1189 | 1190 | // SymmetricDifference returns a new Persons containing all elements present 1191 | // in either s or other but not both. 1192 | func (s *Persons) SymmetricDifference(other *Persons) *Persons { 1193 | items := s.difference(other) 1194 | items = append(items, other.difference(s)...) 1195 | return NewPersons(items...) 1196 | } 1197 | 1198 | // Intersection returns a new Persons containing all elements present in both 1199 | // s and other. 1200 | func (s *Persons) Intersection(other *Persons) *Persons { 1201 | items := make([]Person, 0) 1202 | s.Range(func(item Person) bool { 1203 | if other.Contains(item) { 1204 | items = append(items, item) 1205 | } 1206 | 1207 | return true 1208 | }) 1209 | 1210 | return NewPersons(items...) 1211 | } 1212 | 1213 | // Len returns the number of elements in s. 1214 | func (s *Persons) Len() int { 1215 | return s.backingMap.Len() 1216 | } 1217 | 1218 | // ToNativeSlice returns a native Go slice containing all elements of s. 1219 | func (s *Persons) ToNativeSlice() []Person { 1220 | items := make([]Person, 0, s.Len()) 1221 | s.Range(func(item Person) bool { 1222 | items = append(items, item) 1223 | return true 1224 | }) 1225 | 1226 | return items 1227 | } 1228 | -------------------------------------------------------------------------------- /examples/types.go: -------------------------------------------------------------------------------- 1 | // Package examples contains a couple of examples of generated peds collections 2 | package examples 3 | 4 | // Person is a custom example type that represents a person 5 | type Person struct { 6 | name string 7 | ssn string 8 | address string 9 | } 10 | 11 | //go:generate peds -maps="PersonBySsn" -sets="Persons" -vectors="IntVector" -file=collections.go -pkg=examples 12 | 13 | // Required for go generate it seems 14 | func f() { 15 | } -------------------------------------------------------------------------------- /experience_report.md: -------------------------------------------------------------------------------- 1 | # Experience report 2 | This sums up some of the key experiences and thoughts from [peds](https://github.com/tobgu/peds) and 3 | other projects that I would like to share as an experience report going 4 | into the work of Go 2. 5 | The different subjects described below are listed in the order of 6 | importance to me. Most important on top, less important towards the 7 | bottom. 8 | 9 | ## Background 10 | I don't have excessive experience in Go but have used it for this, as 11 | well as a couple of other projects since about one year. There are 12 | probably a ton of stuff that could be improved in this project, some 13 | of which [I'm aware](https://github.com/tobgu/peds#caveats), and some 14 | which I've not yet come to realize. I'm happy to take suggestions 15 | and PRs! 16 | 17 | As for compiler construction I don't have much experience beyond the 18 | basics so it may be the case that I make overly simplistic assumptions 19 | in my comments below. If so, please bear with me and let me know. 20 | 21 | ### Why Go? 22 | These are some of the reasons I choose to engage with the Go language 23 | in the first place: 24 | * Performance, natively compiled with a memory layout which lets you 25 | write fast, [mechanically sympathetic](https://dzone.com/articles/mechanical-sympathy), programs with low memory overhead. 26 | * Quick compilation. 27 | * Great profiling and diagnostics tooling comes built in. 28 | * Garbage collected, makes it possible to move fast and focus on the 29 | business problem. 30 | * A great, and growing, community. 31 | * A large number of companies heavily invested in Go. 32 | 33 | I specifically did not come to Go because of it being a "simple" 34 | language (as written and read). The lack of expressiveness and 35 | possibility for abstraction in the language was actually one of the 36 | few things that made me reluctant to use Go in the first place. 37 | 38 | I realise that having a small language is an enabler for some of the 39 | compelling reasons of Go that I've listed above such as quick compile 40 | times and good tooling though. 41 | 42 | ## Generics 43 | The collections in peds are of course a prime example of a data 44 | structures that would benefit tremendously from generics in the 45 | language. 46 | 47 | ### Writing the code 48 | When starting the project the choice was between code generation to 49 | make the collections statically type safe and more efficient, and 50 | `interface{}` to make life easier for the implementer (me) but throw 51 | statical type safety and performance over board. 52 | To me it was like choosing between two evils but in the end I opted 53 | for the former because of personal preferences. It would also give 54 | me the chance to explore the pains of writing a code generator. 55 | 56 | I knew that I wanted peds to be a standalone binary for generating the 57 | code as opposed to "generic" code that would then make use of a third 58 | party library such as [genny](https://github.com/cheekybits/genny) to 59 | generate the code. 60 | This was mainly a decision based on usability since relying on a 61 | third party library would be one more thing that the developer 62 | using peds would have to learn. I also wanted to have full control 63 | over the inputs to the code generation, something that would not 64 | be possible with any of the existing tools I looked at. 65 | 66 | With that in mind the generic/templated code would have to reside within 67 | the peds binary. To me that meant the template code would have to be 68 | template strings (I choose go text templates since they are part of 69 | the stdlib). But I did not want to write all the 70 | code within strings. Doing that would mean no help from the IDE/editor or 71 | any of the other tooling that I'm used to as a Go programmer. 72 | To solve this the generic code is written as plain Go code with special, 73 | "magic", names for the generic types and functions. This code is then 74 | processed by a small Python script that turns it into a number of 75 | template strings with suitable template variables that are in turn used 76 | by the code generator. Peds then substitutes the template variables 77 | with data from the command line options and outputs the generated 78 | code to a file and package of the users choice. 79 | 80 | All this is of course painful for the library author. Beyond the 81 | initial setup he must keep track of which those magic names are 82 | where substitution happens. 83 | For the end user the experience should hopefully be fairly smooth 84 | though. It's just one command that can be used together with 85 | `go:generate`. 86 | 87 | ### Access control 88 | Another problem with code generation compared to built in generic 89 | support is that there is no control of where the generated code ends 90 | up. In the case of collections the generated code must also be 91 | aware of the types of the contained data. 92 | 93 | In peds this is currently solved by allowing the user to specify 94 | target file and package together with any imports that may be 95 | needed within the generated code to access the contained types. 96 | While it works, it feels dirty. All functions, types and data 97 | defined in the generated code is potentially at risk of being 98 | accessed by code existing in the same package. For peds this weakens 99 | the promise of immutability somewhat. For the promise to be held 100 | only publicly accessible data and functions must be used. 101 | 102 | ### Requirements on the generic type 103 | For the Map there is a requirement on the key, it must be hashable. 104 | With code generation these kind of requirements 105 | cannot be properly defined. Furthermore, even though all basic types 106 | in Go already supports hashing (for use with the native map) this 107 | functionality is not accessible outside the runtime package. I believe 108 | this has it's reasons and that it has been done to reduce the number 109 | of bugs related to code that does not fulfill the contract between 110 | hash and equality, that said this is an example of decisions in Go 111 | that sometimes make me feel like a third class citizens of the 112 | language. 113 | 114 | Hashing as implemented in peds right now is very basic. If the key type 115 | is a recognized as a basic Go type (int, float, string, ...) a custom, 116 | tailor made, hash function is applied to it. If not, then the object is 117 | printed to a string and that string is in turn hashed. Improvements to 118 | the implementation are certainly possible here. 119 | 120 | Ideally though I would like to delegate the hashing to the type that 121 | is used as key. That would allow it to be implemented efficiently based 122 | on knowledge that is simply not available to peds. 123 | 124 | For this to happen with a generic-like collection, some kind of 125 | constraint on the generic type would have to be expressed. 126 | 127 | ### API Documentation 128 | I was choosing between creating documentation for the generic types 129 | (with the non-standard generic names) or for the concrete, generated, 130 | implementations. None of them are really good. The generic names 131 | will never be referred from the user, the concrete types will often be 132 | quite different from each other. In the end I choose to generated docs 133 | for both as seen here: https://github.com/tobgu/peds#godoc. 134 | 135 | Documentation for APIs taking `interface{}` as parameter is also a bit 136 | tricky. Since the empty interface doesn't say anything the types 137 | won't tell anything. This means that the docs have to be more extensive 138 | (and people may actually have to read them). 139 | 140 | 141 | ### Concluding thoughts 142 | To support the peds use case in Go two things are needed from a 143 | generics like suggestion: 144 | 145 | 1. The ability to instantiate the container for arbitrary type(s) 146 | 2. The ability to restrict those types to such that are hashable and 147 | comparable for the map and set types. 148 | 149 | The below is not a suggestion for how to finally implement generics but 150 | rather an example that would fulfill the needs by peds. 151 | 152 | ``` 153 | peds.Map[K:Hashable, V] 154 | ``` 155 | 156 | Hashable would state that the type of the key needs to be hashable. 157 | To me it seems like the current Go interfaces could serve as a perfect 158 | base for these kind of specifications. While not strictly necessary, 159 | since compilation of the generic type would fail if the `Hash()` 160 | function was missing, it provides a much clearer contract at no 161 | cost for the client since Go interfaces are implemented implicitly 162 | (which I think is great!). 163 | 164 | ``` 165 | type Hashable interface { 166 | Hash() uint64 167 | } 168 | ``` 169 | 170 | This would allow the compiler to generate efficient code avoiding 171 | the runtime indirection usually associated with interface functions 172 | and potentially inline the code in `Hash()` of the key type. 173 | 174 | #### Implementation details 175 | I want to finish with my view on generics implementation. There 176 | is a great document called (Summary of Go Generics discussions)[https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/edit#heading=h.vuko0u3txoew] 177 | that outlines a bunch of considerations regarding generics in Go. In it 178 | a number of different approaches are outlined. 179 | 180 | I'm pro the type specialization approach where specific code is emitted 181 | for every instance of the generic type/function and no boxing takes place. 182 | Rust and C++ are examples of a languages that use this approach. 183 | 184 | I believe boxing (as done in Java for example) would undo 185 | many possible use cases where generics could be used to write 186 | "tight"/"performance critical"/"mechanically sympathetic" algorithms or 187 | data structures. 188 | In Java this may be OK since there's a JIT that can do all sorts of 189 | optimizations in runtime, in Go that is not the case. 190 | While I'm all for the mantra of avoiding premature optimization on the 191 | application level this becomes less and less true the further down the 192 | stack you go since you have less and less context. 193 | With the language being in the bottom levels I think performance is key 194 | to avoid restricting what can be done on higher levels more than necessary. 195 | 196 | With a templating approach the binary will be fatter for sure since more 197 | code is generated. The Go binaries are already fairly fat though and to 198 | me, adding a couple of extra % to that does not really matter. 199 | The negative impact on the instruction cache because of code bloat 200 | that some claim as a potential problem should be tested in reality. 201 | Furthermore, if the compiler/linker only generated code for the functions 202 | used by the client code as opposed to the full API of the generic 203 | type my gut feeling is that the code bloat should not be that dramatic 204 | since in reality many client applications often only use a small subset 205 | of the provided functionality. This would of course make the compiler 206 | more complicated and potentially increase compile times but I trust 207 | the compiler team will solve this. ;-) 208 | 209 | ## Immutability 210 | With the strong focus on concurrency in Go I was surprised that 211 | immutability is not focused on more as a means to write safe 212 | concurrent code. 213 | 214 | While the value semantics of basic types and structs alleviates 215 | some of the problems it does not apply to slices and maps which are 216 | reference types. 217 | 218 | A comment in the code that a variable should be treated as immutable 219 | is not a satisfactory alternative to true language support. I would, 220 | for example, never trust code in an evolving application to adhere 221 | to such a comment if I was debugging an issue (and I'm not alone 222 | it seems, https://github.com/lukechampine/freeze). 223 | 224 | The creation of peds is a way to show case immutable alternatives. 225 | While I realise it may not fit into the core/stdlib of the language 226 | I believe that better options at the language level to specify that 227 | data cannot be changed is core. 228 | 229 | In peds the vector is implemented as a tree of nodes which are 32 230 | elements wide. Each such node is in itself immutable (as in that's 231 | the intention) but I cannot describe this in the language. 232 | This makes bugs in my implementation more likely because the compiler 233 | cannot check that I adhere to my own rules. It also reduces the 234 | possibility to communicate this information to anyone else or my future 235 | self. 236 | 237 | ## Iteration 238 | 239 | ### The need for unification 240 | The use of the `range` keyword to iterate over slices and maps is fine 241 | I think. The only problem is that it's not possible to use with any 242 | other collections than the built in. 243 | That's why people come up with all sorts of ideas for how to do it. The 244 | principal ideas are described nicely here: 245 | https://blog.kowalczyk.info/article/1Bkr/3-ways-to-iterate-in-go.html 246 | 247 | In peds I finally settled on a callback based solution which closely 248 | resembles that of the `sync.Map` that came with Go 1.9 for familiarity. 249 | 250 | The implementation is similar to the below example: 251 | 252 | ``` 253 | type T []int 254 | 255 | func (t T) Range(f func(int) bool) { 256 | for _, x := range t { 257 | ok := f(x) 258 | if !ok { 259 | return 260 | } 261 | } 262 | } 263 | ``` 264 | 265 | I think it's a reasonable solution but it requires the user to define a 266 | callback function, anonymous or named. It's also slower than using 267 | `range` directly since it involves a function call, that the compiler 268 | does not know to inline, for each step in the iteration. 269 | 270 | What I would like to see is a unified way of iterating over any data 271 | structure. This would allow users to learn how to iterate in Go once. 272 | 273 | ### for-loop says nothing 274 | I applause that there is only one loop construct in Go (I don't want 275 | a while loop, a do-while loop, ...) but I think the for-loop is over 276 | used. 277 | 278 | First of all I don't agree that it's simple: 279 | * There are at least four places where you could make an error in a for 280 | loop: 281 | - the init statement 282 | - the condition expression 283 | - the post statement 284 | - the loop body 285 | * Every time I see a for loop I have to look at all parts of it to 286 | determine what it actually does. 287 | 288 | I think that higher level construct such as map, reduce, filter, etc. 289 | are actually good things since they immediately tell me something about 290 | the intention of the iteration. They also reduce the number of places 291 | where mistakes can be made, often you just have to concern yourself with 292 | the loop body. 293 | 294 | ## Sum types/tagged unions 295 | In peds the vector is implemented as a wide tree consisting of 296 | 32-element wide nodes. Internal nodes contain references to lower 297 | level nodes while the leafs contain the actual values contained 298 | in the vector. A node can hence be either of two, and only two, 299 | different types. 300 | I could not come up with a good way of describing this. In the end I 301 | decided to implement the nodes as `{}interface`, a type that in the 302 | code is referred to as `commonNode`. 303 | 304 | When accessing the nodes they are first type asserted back to their 305 | original type. Which type to assert to is determined by helper 306 | variables which keep track of where in the tree you are. 307 | 308 | When inserting new nodes they are first casted to `commonNode`. There 309 | is nothing in the type system preventing me from inserting anything 310 | into a node. Something that would cause other parts of the code to 311 | fail at runtime. The failre would presumably always manifest itself 312 | far away from the root cause since the error would occur in the read 313 | path while the root cause was introduced in the write path. 314 | 315 | A sum type containing either an internal node or a leaf node could 316 | help in this situation since it would restrict the possible errors. 317 | It would also document the tree structure nicely for later maintainers 318 | something that cannot be said about the commonNode since `interface{}` 319 | says nothing. 320 | 321 | As an alternative to using the empty interface I experimented with 322 | a poor mans sum type along the way. Like this: 323 | 324 | ``` 325 | type Node struct { 326 | nodes []Node 327 | data []GenericType 328 | } 329 | ``` 330 | 331 | The idea was that only one of the fields in the struct would ever be 332 | set depending on the type of node. The other field would be `nil`. 333 | The pro of this type compared to the empty interface is that it's now 334 | clearly stated which types make up a valid node. 335 | The con is that it gives the illusion that it's OK to set both fields. 336 | It also occupies more space than the empty interface and performs 337 | slightly worse. 338 | Because of these drawbacks I decided to discard this experiment. 339 | 340 | ## Set 341 | I can't believe there's not a built in generic set type in Go 1! Sets 342 | are great for so many things and I really hate re-implementing them 343 | as a `map[type]struct{}` for every type I need them in Go. 344 | 345 | This is more of an experience in general with the Go language than 346 | specific to the implementation of peds. These previous experiences went 347 | into designing the set type in peds though (which closely mimics the 348 | set API in python). 349 | 350 | ## Round up 351 | I don't think any of the subjects listed above are novel, they have all 352 | been discussed before in various forms. I do believe there's a reason 353 | that they have been discussed before though. They would help to solve 354 | a class of problems a lot more elegantly than what is possible in 355 | Go today. 356 | 357 | I also think that adding some of them (mainly generics) would make the 358 | language more attractive to a large number of programmers who today 359 | dismiss Go because of lack of expressiveness in the type system. 360 | Adding generics would also open up for the creation of a ton of 361 | libraries for data structures and algorithms that nobody bothers 362 | to implement in Go today. This would make the language more useful in 363 | areas and domains where it is not used that much today. While as an 364 | application developer the need for generics may not arise that often, 365 | I think it would allow a lot of applications to stand on the shoulders 366 | of giants (great libraries) to a much greater extent than what is 367 | possible today. 368 | 369 | Finally I would like to stress the importance (to me) of focusing on 370 | performance and mechanical sympathy moving forward to avoid that Go 371 | ends up together with the scripting languages (Python, Ruby, etc) where 372 | anything performance critical has to be implemented as an extension 373 | in a different language. I really want to be able to use Go for 374 | everything, all the way from the tightest loops and up! 375 | 376 | Go is a great language with really nice runtime characteristics, 377 | tooling and a nice deployment story. Lets build on that! :-) 378 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tobgu/peds 2 | 3 | go 1.13 4 | 5 | require github.com/pkg/errors v0.8.1 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 2 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 3 | -------------------------------------------------------------------------------- /go2go/src/peds/containers.go: -------------------------------------------------------------------------------- 1 | // Code generated by go2go; DO NOT EDIT. 2 | 3 | 4 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 5 | package peds 6 | 7 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 8 | import ( 9 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 10 | "fmt" 11 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 12 | "math" 13 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 14 | "runtime" 15 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 16 | "strings" 17 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 18 | "testing" 19 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 20 | "unsafe" 21 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:1 22 | ) 23 | 24 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:5 25 | const shiftSize = 5 26 | const nodeSize = 32 27 | const shiftBitMask = 0x1F 28 | 29 | func uintMin(a, b uint) uint { 30 | if a < b { 31 | return a 32 | } 33 | 34 | return b 35 | } 36 | 37 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:20 38 | type commonNode interface{} 39 | 40 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:21 41 | var emptyCommonNode commonNode = []commonNode{} 42 | 43 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:91 44 | func newPath(shift uint, node commonNode) commonNode { 45 | if shift == 0 { 46 | return node 47 | } 48 | 49 | return newPath(shift-shiftSize, commonNode([]commonNode{node})) 50 | } 51 | 52 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:217 53 | func assertSliceOk(start, stop, len int) { 54 | if start < 0 { 55 | panic(fmt.Sprintf("Invalid slice index %d (index must be non-negative)", start)) 56 | } 57 | 58 | if start > stop { 59 | panic(fmt.Sprintf("Invalid slice index: %d > %d", start, stop)) 60 | } 61 | 62 | if stop > len { 63 | panic(fmt.Sprintf("Slice bounds out of range, start=%d, stop=%d, len=%d", start, stop, len)) 64 | } 65 | } 66 | 67 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:229 68 | type Importable୦ int 69 | 70 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:229 71 | var _ = fmt.Errorf 72 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:229 73 | var _ = math.Abs 74 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:229 75 | var _ = runtime.BlockProfile 76 | 77 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:229 78 | type _ strings.Builder 79 | 80 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:229 81 | var _ = testing.AllocsPerRun 82 | 83 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:229 84 | type _ unsafe.Pointer 85 | -------------------------------------------------------------------------------- /go2go/src/peds/containers.go2: -------------------------------------------------------------------------------- 1 | package peds 2 | 3 | import "fmt" 4 | 5 | const shiftSize = 5 6 | const nodeSize = 32 7 | const shiftBitMask = 0x1F 8 | 9 | func uintMin(a, b uint) uint { 10 | if a < b { 11 | return a 12 | } 13 | 14 | return b 15 | } 16 | 17 | ////////////// 18 | /// Vector /// 19 | ////////////// 20 | type commonNode interface{} 21 | var emptyCommonNode commonNode = []commonNode{} 22 | 23 | 24 | // A Vector is an ordered persistent/immutable collection of items corresponding roughly 25 | // to the use cases for a slice. 26 | type Vector(type T) struct { 27 | tail []T 28 | root commonNode 29 | len uint 30 | shift uint 31 | } 32 | 33 | // NewVector returns a new vector containing the items provided in items. 34 | func NewVector(type T)(items ...T) *Vector(T) { 35 | // TODO: Could potentially do something smarter with a factory for a certain type 36 | // if this results in a lot of allocations. 37 | tail := make([]T, 0) 38 | v := &Vector(T){root: emptyCommonNode, shift: shiftSize, tail: tail} 39 | return v.Append(items...) 40 | } 41 | 42 | // Append returns a new vector with item(s) appended to it. 43 | func (v *Vector(T)) Append(item ...T) *Vector(T) { 44 | result := v 45 | itemLen := uint(len(item)) 46 | for insertOffset := uint(0); insertOffset < itemLen; { 47 | tailLen := result.len - result.tailOffset() 48 | tailFree := nodeSize - tailLen 49 | if tailFree == 0 { 50 | result = result.pushLeafNode(result.tail) 51 | result.tail = make([]T, 0) 52 | tailFree = nodeSize 53 | tailLen = 0 54 | } 55 | 56 | batchLen := uintMin(itemLen-insertOffset, tailFree) 57 | newTail := make([]T, 0, tailLen+batchLen) 58 | newTail = append(newTail, result.tail...) 59 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 60 | result = &Vector(T){root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 61 | insertOffset += batchLen 62 | } 63 | 64 | return result 65 | } 66 | 67 | func (v *Vector(T)) tailOffset() uint { 68 | if v.len < nodeSize { 69 | return 0 70 | } 71 | 72 | return ((v.len - 1) >> shiftSize) << shiftSize 73 | } 74 | 75 | func (v *Vector(T)) pushLeafNode(node []T) *Vector(T) { 76 | var newRoot commonNode 77 | newShift := v.shift 78 | 79 | // Root overflow? 80 | if (v.len >> shiftSize) > (1 << v.shift) { 81 | newNode := newPath(v.shift, node) 82 | newRoot = commonNode([]commonNode{v.root, newNode}) 83 | newShift = v.shift + shiftSize 84 | } else { 85 | newRoot = v.pushTail(v.shift, v.root, node) 86 | } 87 | 88 | return &Vector(T){root: newRoot, tail: v.tail, len: v.len, shift: newShift} 89 | } 90 | 91 | func newPath(shift uint, node commonNode) commonNode { 92 | if shift == 0 { 93 | return node 94 | } 95 | 96 | return newPath(shift-shiftSize, commonNode([]commonNode{node})) 97 | } 98 | 99 | func (v *Vector(T)) pushTail(level uint, parent commonNode, tailNode []T) commonNode { 100 | subIdx := ((v.len - 1) >> level) & shiftBitMask 101 | parentNode := parent.([]commonNode) 102 | ret := make([]commonNode, subIdx+1) 103 | copy(ret, parentNode) 104 | var nodeToInsert commonNode 105 | 106 | if level == shiftSize { 107 | nodeToInsert = tailNode 108 | } else if subIdx < uint(len(parentNode)) { 109 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 110 | } else { 111 | nodeToInsert = newPath(level-shiftSize, tailNode) 112 | } 113 | 114 | ret[subIdx] = nodeToInsert 115 | return ret 116 | } 117 | 118 | // Len returns the length of v. 119 | func (v *Vector(T)) Len() int { 120 | return int(v.len) 121 | } 122 | 123 | // Get returns the element at position i. 124 | func (v *Vector(T)) Get(i int) T { 125 | if i < 0 || uint(i) >= v.len { 126 | panic("Index out of bounds") 127 | } 128 | 129 | return v.sliceFor(uint(i))[i&shiftBitMask] 130 | } 131 | 132 | func (v *Vector(T)) sliceFor(i uint) []T { 133 | if i >= v.tailOffset() { 134 | return v.tail 135 | } 136 | 137 | node := v.root 138 | for level := v.shift; level > 0; level -= shiftSize { 139 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 140 | } 141 | 142 | // TODO: Change the nodes of this type to be 32 element arrays of T rather than 143 | // slices to get rid of some overhead? 144 | return node.([]T) 145 | } 146 | 147 | 148 | // Set returns a new vector with the element at position i set to item. 149 | func (v *Vector(T)) Set(i int, item T) *Vector(T) { 150 | if i < 0 || uint(i) >= v.len { 151 | panic("Index out of bounds") 152 | } 153 | 154 | if uint(i) >= v.tailOffset() { 155 | newTail := make([]T, len(v.tail)) 156 | copy(newTail, v.tail) 157 | newTail[i&shiftBitMask] = item 158 | return &Vector(T){root: v.root, tail: newTail, len: v.len, shift: v.shift} 159 | } 160 | 161 | return &Vector(T){root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 162 | } 163 | 164 | 165 | func (v *Vector(T)) doAssoc(level uint, node commonNode, i uint, item T) commonNode { 166 | if level == 0 { 167 | ret := make([]T, nodeSize) 168 | copy(ret, node.([]T)) 169 | ret[i&shiftBitMask] = item 170 | return ret 171 | } 172 | 173 | ret := make([]commonNode, nodeSize) 174 | copy(ret, node.([]commonNode)) 175 | subidx := (i >> level) & shiftBitMask 176 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 177 | return ret 178 | } 179 | 180 | // Range calls f repeatedly passing it each element in v in order as argument until either 181 | // all elements have been visited or f returns false. 182 | func (v *Vector(T)) Range(f func(T) bool) { 183 | var currentNode []T 184 | for i := uint(0); i < v.len; i++ { 185 | if i&shiftBitMask == 0 { 186 | currentNode = v.sliceFor(i) 187 | } 188 | 189 | if !f(currentNode[i&shiftBitMask]) { 190 | return 191 | } 192 | } 193 | } 194 | 195 | // Slice returns a VectorSlice that refers to all elements [start,stop) in v. 196 | func (v *Vector(T)) Slice(start, stop int) *VectorSlice(T) { 197 | assertSliceOk(start, stop, v.Len()) 198 | return &VectorSlice(T){vector: v, start: start, stop: stop} 199 | } 200 | 201 | 202 | // ToNativeSlice returns a Go slice containing all elements of v 203 | func (v *Vector(T)) ToNativeSlice() []T { 204 | result := make([]T, 0, v.len) 205 | for i := uint(0); i < v.len; i += nodeSize { 206 | result = append(result, v.sliceFor(i)...) 207 | } 208 | 209 | return result 210 | } 211 | 212 | 213 | //////////////// 214 | //// Slice ///// 215 | //////////////// 216 | 217 | func assertSliceOk(start, stop, len int) { 218 | if start < 0 { 219 | panic(fmt.Sprintf("Invalid slice index %d (index must be non-negative)", start)) 220 | } 221 | 222 | if start > stop { 223 | panic(fmt.Sprintf("Invalid slice index: %d > %d", start, stop)) 224 | } 225 | 226 | if stop > len { 227 | panic(fmt.Sprintf("Slice bounds out of range, start=%d, stop=%d, len=%d", start, stop, len)) 228 | } 229 | } 230 | 231 | // VectorSlice is a slice type backed by a Vector. 232 | type VectorSlice(type T) struct { 233 | vector *Vector(T) 234 | start, stop int 235 | } 236 | 237 | // NewVectorSlice returns a new NewVectorSlice containing the items provided in items. 238 | func NewVectorSlice(type T)(items ...T) *VectorSlice(T) { 239 | return &VectorSlice(T){vector: NewVector(T)(items...), start: 0, stop: len(items)} 240 | } 241 | 242 | // Len returns the length of s. 243 | func (s *VectorSlice(T)) Len() int { 244 | return s.stop - s.start 245 | } 246 | 247 | // Get returns the element at position i. 248 | func (s *VectorSlice(T)) Get(i int) T { 249 | if i < 0 || s.start+i >= s.stop { 250 | panic("Index out of bounds") 251 | } 252 | 253 | return s.vector.Get(s.start + i) 254 | } 255 | 256 | // Set returns a new slice with the element at position i set to item. 257 | func (s *VectorSlice(T)) Set(i int, item T) *VectorSlice(T) { 258 | if i < 0 || s.start+i >= s.stop { 259 | panic("Index out of bounds") 260 | } 261 | 262 | return s.vector.Set(s.start+i, item).Slice(s.start, s.stop) 263 | } 264 | 265 | // Append returns a new slice with item(s) appended to it. 266 | func (s *VectorSlice(T)) Append(items ...T) *VectorSlice(T) { 267 | newSlice := VectorSlice(T){vector: s.vector, start: s.start, stop: s.stop + len(items)} 268 | 269 | // If this is v slice that has an upper bound that is lower than the backing 270 | // vector then set the values in the backing vector to achieve some structural 271 | // sharing. 272 | itemPos := 0 273 | for ; s.stop+itemPos < s.vector.Len() && itemPos < len(items); itemPos++ { 274 | newSlice.vector = newSlice.vector.Set(s.stop+itemPos, items[itemPos]) 275 | } 276 | 277 | // For the rest just append it to the underlying vector 278 | newSlice.vector = newSlice.vector.Append(items[itemPos:]...) 279 | return &newSlice 280 | } 281 | 282 | // Slice returns a VectorSlice that refers to all elements [start,stop) in s. 283 | func (s *VectorSlice(T)) Slice(start, stop int) *VectorSlice(T) { 284 | assertSliceOk(start, stop, s.stop-s.start) 285 | return &VectorSlice(T){vector: s.vector, start: s.start + start, stop: s.start + stop} 286 | } 287 | 288 | // Range calls f repeatedly passing it each element in s in order as argument until either 289 | // all elements have been visited or f returns false. 290 | func (s *VectorSlice(T)) Range(f func(T) bool) { 291 | var currentNode []T 292 | for i := uint(s.start); i < uint(s.stop); i++ { 293 | if i&shiftBitMask == 0 || i == uint(s.start) { 294 | currentNode = s.vector.sliceFor(uint(i)) 295 | } 296 | 297 | if !f(currentNode[i&shiftBitMask]) { 298 | return 299 | } 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /go2go/src/peds/map.go: -------------------------------------------------------------------------------- 1 | // Code generated by go2go; DO NOT EDIT. 2 | 3 | 4 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 5 | package peds 6 | 7 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 8 | import ( 9 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 10 | "fmt" 11 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 12 | "math" 13 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 14 | "runtime" 15 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 16 | "strings" 17 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 18 | "testing" 19 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 20 | "unsafe" 21 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:1 22 | ) 23 | 24 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:8 25 | const upperMapLoadFactor float64 = 8.0 26 | const lowerMapLoadFactor float64 = 2.0 27 | const initialMapLoadFactor float64 = (upperMapLoadFactor + lowerMapLoadFactor) / 2 28 | 29 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:19 30 | func nilinterhash(p unsafe.Pointer, h uintptr) uintptr { return 0 } 31 | 32 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:22 33 | func genericHash(x interface{}) uint32 { 34 | return uint32(nilinterhash(unsafe.Pointer(&x), 0)) 35 | } 36 | 37 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:24 38 | var _ = fmt.Errorf 39 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:24 40 | var _ = math.Abs 41 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:24 42 | var _ = runtime.BlockProfile 43 | 44 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:24 45 | type _ strings.Builder 46 | 47 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:24 48 | var _ = testing.AllocsPerRun 49 | 50 | //line /home/tobias/Development/go/peds/go2go/src/peds/map.go2:24 51 | type _ unsafe.Pointer 52 | -------------------------------------------------------------------------------- /go2go/src/peds/map.go2: -------------------------------------------------------------------------------- 1 | package peds 2 | 3 | import ( 4 | "math" 5 | "unsafe" 6 | ) 7 | 8 | const upperMapLoadFactor float64 = 8.0 9 | const lowerMapLoadFactor float64 = 2.0 10 | const initialMapLoadFactor float64 = (upperMapLoadFactor + lowerMapLoadFactor) / 2 11 | 12 | type MapItem(type K comparable, V interface{}) struct { 13 | Key K 14 | Value V 15 | } 16 | 17 | //go:noescape 18 | //go:linkname nilinterhash runtime.nilinterhash 19 | func nilinterhash(p unsafe.Pointer, h uintptr) uintptr { return 0 } 20 | 21 | // TODO: Try to avoid interfaces for hashing 22 | func genericHash(x interface{}) uint32 { 23 | return uint32(nilinterhash(unsafe.Pointer(&x), 0)) 24 | } 25 | 26 | type privateItemBucket(type K comparable, V interface{}) []MapItem(K, V) 27 | 28 | // Helper type used during map creation and reallocation 29 | type privateItemBuckets(type K comparable, V interface{}) struct { 30 | buckets []privateItemBucket(K, V) 31 | length int 32 | } 33 | 34 | func newPrivateItemBuckets(type K comparable, V interface{})(itemCount int) *privateItemBuckets(K, V) { 35 | size := int(float64(itemCount)/initialMapLoadFactor) + 1 36 | 37 | // TODO: The need for parenthesis below are slightly surprising 38 | buckets := make([](privateItemBucket(K, V)), size) 39 | return &privateItemBuckets(K, V){buckets: buckets} 40 | } 41 | 42 | type Map(type K comparable, V interface{}) struct { 43 | backingVector *Vector(privateItemBucket(K, V)) 44 | len int 45 | } 46 | 47 | func (b *privateItemBuckets(K, V)) AddItem(item MapItem(K, V)) { 48 | ix := int(uint64(genericHash(item.Key)) % uint64(len(b.buckets))) 49 | bucket := b.buckets[ix] 50 | if bucket != nil { 51 | // Hash collision, merge with existing bucket 52 | for keyIx, bItem := range bucket { 53 | if item.Key == bItem.Key { 54 | bucket[keyIx] = item 55 | return 56 | } 57 | } 58 | 59 | b.buckets[ix] = append(bucket, MapItem(K, V){Key: item.Key, Value: item.Value}) 60 | b.length++ 61 | } else { 62 | bucket := make(privateItemBucket(K, V), 0, int(math.Max(initialMapLoadFactor, 1.0))) 63 | b.buckets[ix] = append(bucket, item) 64 | b.length++ 65 | } 66 | } 67 | 68 | func (b *privateItemBuckets(K, V)) AddItemsFromMap(m *Map(K, V)) { 69 | m.backingVector.Range(func(bucket privateItemBucket(K, V)) bool { 70 | for _, item := range bucket { 71 | b.AddItem(item) 72 | } 73 | return true 74 | }) 75 | } 76 | 77 | func newMap(type K comparable, V interface{})(items []MapItem(K, V)) *Map(K, V) { 78 | buckets := newPrivateItemBuckets(K, V)(len(items)) 79 | for _, item := range items { 80 | buckets.AddItem(item) 81 | } 82 | return &Map(K, V){backingVector: NewVector(buckets.buckets...), len: buckets.length} 83 | } 84 | 85 | // NewMap returns a new map containing all items in items. 86 | func NewMap(type K comparable, V interface{})(items ...MapItem(K, V)) *Map(K, V) { 87 | return newMap(items) 88 | } 89 | 90 | // NewMapFromNativeMap returns a new Map containing all items in m. 91 | func NewMapFromNativeMap(type K comparable, V interface{})(m map[K]V) *Map(K, V) { 92 | buckets := newPrivateItemBuckets(K, V)(len(m)) 93 | for key, value := range m { 94 | buckets.AddItem(MapItem(K, V){Key: key, Value: value}) 95 | } 96 | 97 | return &Map(K, V){backingVector: NewVector(buckets.buckets...), len: buckets.length} 98 | } 99 | 100 | // Len returns the number of items in m. 101 | func (m *Map(K, V)) Len() int { 102 | return int(m.len) 103 | } 104 | 105 | func (m *Map(K, V)) pos(key K) int { 106 | return int(uint64(genericHash(key)) % uint64(m.backingVector.Len())) 107 | } 108 | 109 | // Load returns value identified by key. ok is set to true if key exists in the map, false otherwise. 110 | func (m *Map(K, V)) Load(key K) (value V, ok bool) { 111 | bucket := m.backingVector.Get(m.pos(key)) 112 | if bucket != nil { 113 | for _, item := range bucket { 114 | if item.Key == key { 115 | return item.Value, true 116 | } 117 | } 118 | } 119 | 120 | var zeroValue V 121 | return zeroValue, false 122 | } 123 | 124 | // Store returns a new Map(K, V) containing value identified by key. 125 | func (m *Map(K, V)) Store(key K, value V) *Map(K, V) { 126 | // Grow backing vector if load factor is too high 127 | if m.Len() >= m.backingVector.Len()*int(upperMapLoadFactor) { 128 | buckets := newPrivateItemBuckets(K, V)(m.Len() + 1) 129 | buckets.AddItemsFromMap(m) 130 | buckets.AddItem(MapItem(K, V){Key: key, Value: value}) 131 | return &Map(K, V){backingVector: NewVector(privateItemBucket(K, V))(buckets.buckets...), len: buckets.length} 132 | } 133 | 134 | pos := m.pos(key) 135 | bucket := m.backingVector.Get(pos) 136 | if bucket != nil { 137 | for ix, item := range bucket { 138 | if item.Key == key { 139 | // Overwrite existing item 140 | newBucket := make(privateItemBucket(K, V), len(bucket)) 141 | copy(newBucket, bucket) 142 | newBucket[ix] = MapItem(K, V){Key: key, Value: value} 143 | return &Map(K, V){backingVector: m.backingVector.Set(pos, newBucket), len: m.len} 144 | } 145 | } 146 | 147 | // Add new item to bucket 148 | newBucket := make(privateItemBucket(K, V), len(bucket), len(bucket)+1) 149 | copy(newBucket, bucket) 150 | newBucket = append(newBucket, MapItem(K, V){Key: key, Value: value}) 151 | return &Map(K, V){backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 152 | } 153 | 154 | item := MapItem(K, V){Key: key, Value: value} 155 | newBucket := privateItemBucket(K, V){item} 156 | return &Map(K, V){backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 157 | } 158 | 159 | // Delete returns a new Map(K, V) without the element identified by key. 160 | func (m *Map(K, V)) Delete(key K) *Map(K, V) { 161 | pos := m.pos(key) 162 | bucket := m.backingVector.Get(pos) 163 | if bucket != nil { 164 | newBucket := make(privateItemBucket(K, V), 0) 165 | for _, item := range bucket { 166 | if item.Key != key { 167 | newBucket = append(newBucket, item) 168 | } 169 | } 170 | 171 | removedItemCount := len(bucket) - len(newBucket) 172 | if removedItemCount == 0 { 173 | return m 174 | } 175 | 176 | if len(newBucket) == 0 { 177 | newBucket = nil 178 | } 179 | 180 | newMap := &Map(K, V){backingVector: m.backingVector.Set(pos, newBucket), len: m.len - removedItemCount} 181 | if newMap.backingVector.Len() > 1 && newMap.Len() < newMap.backingVector.Len()*int(lowerMapLoadFactor) { 182 | // Shrink backing vector if needed to avoid occupying excessive space 183 | buckets := newPrivateItemBuckets(K, V)(newMap.Len()) 184 | buckets.AddItemsFromMap(newMap) 185 | return &Map(K, V){backingVector: NewVector(buckets.buckets...), len: buckets.length} 186 | } 187 | 188 | return newMap 189 | } 190 | 191 | return m 192 | } 193 | 194 | // Range calls f repeatedly passing it each key and value as argument until either 195 | // all elements have been visited or f returns false. 196 | func (m *Map(K, V)) Range(f func(K, V) bool) { 197 | m.backingVector.Range(func(bucket privateItemBucket(K, V)) bool { 198 | for _, item := range bucket { 199 | if !f(item.Key, item.Value) { 200 | return false 201 | } 202 | } 203 | return true 204 | }) 205 | } 206 | 207 | // ToNativeMap returns a native Go map containing all elements of m. 208 | func (m *Map(K, V)) ToNativeMap() map[K]V { 209 | result := make(map[K]V) 210 | m.Range(func(key K, value V) bool { 211 | result[key] = value 212 | return true 213 | }) 214 | 215 | return result 216 | } 217 | -------------------------------------------------------------------------------- /go2go/src/peds/map_test.go2: -------------------------------------------------------------------------------- 1 | package peds 2 | 3 | import ( 4 | "testing" 5 | "fmt" 6 | ) 7 | 8 | func TestLenOfNewMap(t *testing.T) { 9 | m := NewMap(string, int)() 10 | assertEqual(t, 0, m.Len()) 11 | 12 | m2 := NewMap(string, int)(MapItem(string, int){Key: "a", Value: 1}) 13 | assertEqual(t, 1, m2.Len()) 14 | 15 | m3 := NewMap(string, int)(MapItem(string, int){Key: "a", Value: 1}, MapItem(string, int){Key: "b", Value: 2}) 16 | assertEqual(t, 2, m3.Len()) 17 | } 18 | 19 | func TestLoadAndStore(t *testing.T) { 20 | m := NewMap(string, int)() 21 | 22 | m2 := m.Store("a", 1) 23 | assertEqual(t, 0, m.Len()) 24 | assertEqual(t, 1, m2.Len()) 25 | 26 | v, ok := m.Load("a") 27 | assertEqual(t, 0, v) 28 | assertEqualBool(t, false, ok) 29 | 30 | v, ok = m2.Load("a") 31 | assertEqual(t, 1, v) 32 | assertEqualBool(t, true, ok) 33 | } 34 | 35 | func TestLoadAndStoreIntKey(t *testing.T) { 36 | m := NewMap(int, string)() 37 | 38 | m2 := m.Store(1, "") 39 | v, _ := m.Load(2) 40 | assertEqualString(t, "", v) 41 | 42 | v, _ = m2.Load(1) 43 | assertEqualString(t, "", v) 44 | } 45 | 46 | func TestLoadAndDeleteExistingItem(t *testing.T) { 47 | m := NewMap(string, int)() 48 | m2 := m.Store("a", 1) 49 | m3 := m.Delete("a") 50 | 51 | assertEqual(t, 0, m3.Len()) 52 | assertEqual(t, 1, m2.Len()) 53 | 54 | v, ok := m2.Load("a") 55 | assertEqualBool(t, true, ok) 56 | assertEqual(t, 1, v) 57 | 58 | v, ok = m3.Load("a") 59 | assertEqualBool(t, false, ok) 60 | assertEqual(t, 0, v) 61 | } 62 | 63 | func TestLoadAndDeleteNonExistingItem(t *testing.T) { 64 | m := NewMap(string, int)() 65 | m2 := m.Store("a", 1) 66 | m3 := m2.Delete("b") 67 | 68 | assertEqual(t, 1, m3.Len()) 69 | assertEqual(t, 1, m2.Len()) 70 | 71 | v, ok := m2.Load("a") 72 | assertEqualBool(t, true, ok) 73 | assertEqual(t, 1, v) 74 | 75 | if m2 != m3 { 76 | t.Errorf("m2 and m3 are not the same object: %p != %p", m2, m3) 77 | } 78 | } 79 | 80 | func TestRangeAllItems(t *testing.T) { 81 | m := NewMap(string, int)(MapItem(string, int){Key: "a", Value: 1}, MapItem(string, int){Key: "b", Value: 2}, MapItem(string, int){Key: "c", Value: 3}) 82 | sum := 0 83 | m.Range(func(key string, value int) bool { 84 | sum += value 85 | return true 86 | }) 87 | assertEqual(t, 6, sum) 88 | } 89 | 90 | func TestRangeStopOnKey(t *testing.T) { 91 | m := NewMap(string, int)(MapItem(string, int){Key: "a", Value: 1}, MapItem(string, int){Key: "b", Value: 2}, MapItem(string, int){Key: "c", Value: 3}) 92 | count := 0 93 | m.Range(func(key string, value int) bool { 94 | if key == "c" || key == "b" { 95 | return false 96 | } 97 | 98 | count++ 99 | return true 100 | }) 101 | 102 | if count > 1 { 103 | t.Errorf("Did not expect count to be more than 1") 104 | } 105 | } 106 | 107 | func TestLargeInsertLookupDelete(t *testing.T) { 108 | // Is 50000 in original test but that seems crazy slow. 109 | // More vector allocations, worse generic hash function, any other culprits? 110 | size := 500 111 | m := NewMap(string, int)() 112 | for j := 0; j < size; j++ { 113 | m = m.Store(fmt.Sprintf("%d", j), j) 114 | } 115 | 116 | for j := 0; j < size; j++ { 117 | v, ok := m.Load(fmt.Sprintf("%d", j)) 118 | assertEqualBool(t, true, ok) 119 | assertEqual(t, v, j) 120 | } 121 | 122 | for j := 0; j < size; j++ { 123 | key := fmt.Sprintf("%d", j) 124 | m = m.Delete(key) 125 | assertEqual(t, size-j-1, m.Len()) 126 | _, ok := m.Load(key) 127 | assertEqualBool(t, false, ok) 128 | } 129 | } 130 | 131 | func TestFromToNativeMap(t *testing.T) { 132 | input := map[string]int{ 133 | "a": 1, 134 | "b": 2, 135 | "c": 3} 136 | m := NewMapFromNativeMap(string, int)(input) 137 | output := m.ToNativeMap() 138 | assertEqual(t, len(input), len(output)) 139 | for key, value := range input { 140 | assertEqual(t, value, output[key]) 141 | } 142 | } -------------------------------------------------------------------------------- /go2go/src/peds/vector_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by go2go; DO NOT EDIT. 2 | 3 | 4 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 5 | package peds 6 | 7 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 8 | import ( 9 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 10 | "fmt" 11 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 12 | "math" 13 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 14 | "runtime" 15 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 16 | "strings" 17 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 18 | "testing" 19 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 20 | "unsafe" 21 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:1 22 | ) 23 | 24 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:13 25 | func TestCanBeInstantiated(t *testing.T) { 26 | v := instantiate୦୦NewVector୦int(1, 2, 3) 27 | v2 := v.Append(4) 28 | if v2.Len() != 4 { 29 | t.Errorf("Expected %d != Expected 4", v2.Len()) 30 | } 31 | 32 | for i := 0; i < 4; i++ { 33 | actual, expected := v2.Get(i), i+1 34 | if actual != expected { 35 | t.Errorf("Actual %d != Expected %d", actual, expected) 36 | } 37 | } 38 | } 39 | 40 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:32 41 | func assertEqual(t *testing.T, expected, actual int) { 42 | if expected != actual { 43 | _, file, line, _ := runtime.Caller(1) 44 | t.Errorf("Error %s, line %d. Expected: %d, actual: %d", file, line, expected, actual) 45 | } 46 | } 47 | 48 | func assertEqualString(t *testing.T, expected, actual string) { 49 | if expected != actual { 50 | _, file, line, _ := runtime.Caller(1) 51 | t.Errorf("Error %s, line %d. Expected: %v, actual: %v", file, line, expected, actual) 52 | } 53 | } 54 | 55 | func assertEqualBool(t *testing.T, expected, actual bool) { 56 | if expected != actual { 57 | _, file, line, _ := runtime.Caller(1) 58 | t.Errorf("Error %s, line %d. Expected: %v, actual: %v", file, line, expected, actual) 59 | } 60 | } 61 | 62 | func assertPanic(t *testing.T, expectedMsg string) { 63 | if r := recover(); r == nil { 64 | _, _, line, _ := runtime.Caller(1) 65 | t.Errorf("Did not raise, line %d.", line) 66 | } else { 67 | msg := r.(string) 68 | if !strings.Contains(msg, expectedMsg) { 69 | t.Errorf("Msg '%s', did not contain '%s'", msg, expectedMsg) 70 | } 71 | } 72 | } 73 | 74 | func inputSlice(start, size int) []int { 75 | result := make([]int, 0, size) 76 | for i := start; i < start+size; i++ { 77 | result = append(result, i) 78 | } 79 | 80 | return result 81 | } 82 | 83 | var testSizes = []int{0, 1, 20, 32, 33, 50, 500, 32 * 32, 32*32 + 1, 10000, 32 * 32 * 32, 32*32*32 + 1} 84 | 85 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:80 86 | func TestPropertiesOfNewVector(t *testing.T) { 87 | for _, l := range testSizes { 88 | t.Run(fmt.Sprintf("NewVector %d", l), func(t *testing.T) { 89 | vec := instantiate୦୦NewVector୦int(inputSlice(0, l)...) 90 | assertEqual(t, vec.Len(), l) 91 | for i := 0; i < l; i++ { 92 | assertEqual(t, i, vec.Get(i)) 93 | } 94 | }) 95 | } 96 | } 97 | 98 | func TestSetItem(t *testing.T) { 99 | for _, l := range testSizes { 100 | t.Run(fmt.Sprintf("Set %d", l), func(t *testing.T) { 101 | vec := instantiate୦୦NewVector୦int(inputSlice(0, l)...) 102 | for i := 0; i < l; i++ { 103 | newArr := vec.Set(i, -i) 104 | assertEqual(t, -i, newArr.Get(i)) 105 | assertEqual(t, i, vec.Get(i)) 106 | } 107 | }) 108 | } 109 | } 110 | 111 | func TestAppend(t *testing.T) { 112 | for _, l := range testSizes { 113 | vec := instantiate୦୦NewVector୦int(inputSlice(0, l)...) 114 | t.Run(fmt.Sprintf("Append %d", l), func(t *testing.T) { 115 | for i := 0; i < 70; i++ { 116 | newVec := vec.Append(inputSlice(l, i)...) 117 | assertEqual(t, i+l, newVec.Len()) 118 | for j := 0; j < i+l; j++ { 119 | assertEqual(t, j, newVec.Get(j)) 120 | } 121 | 122 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:117 123 | assertEqual(t, l, vec.Len()) 124 | } 125 | }) 126 | } 127 | } 128 | 129 | func TestVectorSetOutOfBoundsNegative(t *testing.T) { 130 | defer assertPanic(t, "Index out of bounds") 131 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:124 132 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Set(-1, 0) 133 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:126 134 | } 135 | 136 | func TestVectorSetOutOfBoundsBeyondEnd(t *testing.T) { 137 | defer assertPanic(t, "Index out of bounds") 138 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:129 139 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Set(10, 0) 140 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:131 141 | } 142 | 143 | func TestVectorGetOutOfBoundsNegative(t *testing.T) { 144 | defer assertPanic(t, "Index out of bounds") 145 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:134 146 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Get(-1) 147 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:136 148 | } 149 | 150 | func TestVectorGetOutOfBoundsBeyondEnd(t *testing.T) { 151 | defer assertPanic(t, "Index out of bounds") 152 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:139 153 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Get(10) 154 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:141 155 | } 156 | 157 | func TestVectorSliceOutOfBounds(t *testing.T) { 158 | tests := []struct { 159 | start, stop int 160 | msg string 161 | }{ 162 | {-1, 3, "Invalid slice index"}, 163 | {0, 11, "Slice bounds out of range"}, 164 | {5, 3, "Invalid slice index"}, 165 | } 166 | 167 | for _, s := range tests { 168 | t.Run(fmt.Sprintf("start=%d, stop=%d", s.start, s.stop), func(t *testing.T) { 169 | defer assertPanic(t, s.msg) 170 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:155 171 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Slice(s.start, s.stop) 172 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:157 173 | }) 174 | } 175 | } 176 | 177 | func TestCompleteIteration(t *testing.T) { 178 | input := inputSlice(0, 10000) 179 | dst := make([]int, 0, 10000) 180 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:163 181 | instantiate୦୦NewVector୦int(input...).Range(func(elem int) bool { 182 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:165 183 | dst = append(dst, elem) 184 | return true 185 | }) 186 | 187 | assertEqual(t, len(input), len(dst)) 188 | assertEqual(t, input[0], dst[0]) 189 | assertEqual(t, input[5000], dst[5000]) 190 | assertEqual(t, input[9999], dst[9999]) 191 | } 192 | 193 | func TestCanceledIteration(t *testing.T) { 194 | input := inputSlice(0, 10000) 195 | count := 0 196 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:177 197 | instantiate୦୦NewVector୦int(input...).Range(func(elem int) bool { 198 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:179 199 | count++ 200 | if count == 5 { 201 | return false 202 | } 203 | return true 204 | }) 205 | 206 | assertEqual(t, 5, count) 207 | } 208 | 209 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:193 210 | func TestSliceIndexes(t *testing.T) { 211 | vec := instantiate୦୦NewVector୦int(inputSlice(0, 1000)...) 212 | slice := vec.Slice(0, 10) 213 | assertEqual(t, 1000, vec.Len()) 214 | assertEqual(t, 10, slice.Len()) 215 | assertEqual(t, 0, slice.Get(0)) 216 | assertEqual(t, 9, slice.Get(9)) 217 | 218 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:202 219 | slice2 := slice.Slice(3, 7) 220 | assertEqual(t, 10, slice.Len()) 221 | assertEqual(t, 4, slice2.Len()) 222 | assertEqual(t, 3, slice2.Get(0)) 223 | assertEqual(t, 6, slice2.Get(3)) 224 | } 225 | 226 | func TestSliceCreation(t *testing.T) { 227 | sliceLen := 10000 228 | slice := instantiate୦୦NewVectorSlice୦int(inputSlice(0, sliceLen)...) 229 | assertEqual(t, slice.Len(), sliceLen) 230 | for i := 0; i < sliceLen; i++ { 231 | assertEqual(t, i, slice.Get(i)) 232 | } 233 | } 234 | 235 | func TestSliceSet(t *testing.T) { 236 | vector := instantiate୦୦NewVector୦int(inputSlice(0, 1000)...) 237 | slice := vector.Slice(10, 100) 238 | slice2 := slice.Set(5, 123) 239 | 240 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:225 241 | assertEqual(t, 15, vector.Get(15)) 242 | assertEqual(t, 15, slice.Get(5)) 243 | assertEqual(t, 123, slice2.Get(5)) 244 | } 245 | 246 | func TestSliceAppendInTheMiddleOfBackingVector(t *testing.T) { 247 | vector := instantiate୦୦NewVector୦int(inputSlice(0, 100)...) 248 | slice := vector.Slice(0, 50) 249 | slice2 := slice.Append(inputSlice(0, 10)...) 250 | 251 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:236 252 | assertEqual(t, 60, slice2.Len()) 253 | assertEqual(t, 0, slice2.Get(50)) 254 | assertEqual(t, 9, slice2.Get(59)) 255 | 256 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:241 257 | assertEqual(t, 50, slice.Len()) 258 | 259 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:244 260 | assertEqual(t, 100, vector.Len()) 261 | assertEqual(t, 50, vector.Get(50)) 262 | assertEqual(t, 59, vector.Get(59)) 263 | } 264 | 265 | func TestSliceAppendAtTheEndOfBackingVector(t *testing.T) { 266 | vector := instantiate୦୦NewVector୦int(inputSlice(0, 100)...) 267 | slice := vector.Slice(0, 100) 268 | slice2 := slice.Append(inputSlice(0, 10)...) 269 | 270 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:255 271 | assertEqual(t, 110, slice2.Len()) 272 | assertEqual(t, 0, slice2.Get(100)) 273 | assertEqual(t, 9, slice2.Get(109)) 274 | 275 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:260 276 | assertEqual(t, 100, slice.Len()) 277 | } 278 | 279 | func TestSliceAppendAtMiddleToEndOfBackingVector(t *testing.T) { 280 | vector := instantiate୦୦NewVector୦int(inputSlice(0, 100)...) 281 | slice := vector.Slice(0, 50) 282 | slice2 := slice.Append(inputSlice(0, 100)...) 283 | 284 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:269 285 | assertEqual(t, 150, slice2.Len()) 286 | assertEqual(t, 0, slice2.Get(50)) 287 | assertEqual(t, 99, slice2.Get(149)) 288 | 289 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:274 290 | assertEqual(t, 50, slice.Len()) 291 | } 292 | 293 | func TestSliceCompleteIteration(t *testing.T) { 294 | vec := instantiate୦୦NewVector୦int(inputSlice(0, 1000)...) 295 | dst := make([]int, 0) 296 | 297 | vec.Slice(5, 200).Range(func(elem int) bool { 298 | dst = append(dst, elem) 299 | return true 300 | }) 301 | 302 | assertEqual(t, 195, len(dst)) 303 | assertEqual(t, 5, dst[0]) 304 | assertEqual(t, 55, dst[50]) 305 | assertEqual(t, 199, dst[194]) 306 | } 307 | 308 | func TestSliceCanceledIteration(t *testing.T) { 309 | vec := instantiate୦୦NewVector୦int(inputSlice(0, 1000)...) 310 | count := 0 311 | 312 | vec.Slice(5, 200).Range(func(elem int) bool { 313 | count++ 314 | if count == 5 { 315 | return false 316 | } 317 | 318 | return true 319 | }) 320 | 321 | assertEqual(t, 5, count) 322 | } 323 | 324 | func TestSliceSetOutOfBoundsNegative(t *testing.T) { 325 | defer assertPanic(t, "Index out of bounds") 326 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:309 327 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Slice(2, 5).Set(-1, 0) 328 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:311 329 | } 330 | 331 | func TestSliceSetOutOfBoundsBeyondEnd(t *testing.T) { 332 | defer assertPanic(t, "Index out of bounds") 333 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:314 334 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Slice(2, 5).Set(4, 0) 335 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:316 336 | } 337 | 338 | func TestSliceGetOutOfBoundsNegative(t *testing.T) { 339 | defer assertPanic(t, "Index out of bounds") 340 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:319 341 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Slice(2, 5).Get(-1) 342 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:321 343 | } 344 | 345 | func TestSliceGetOutOfBoundsBeyondEnd(t *testing.T) { 346 | defer assertPanic(t, "Index out of bounds") 347 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:324 348 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Slice(2, 5).Get(4) 349 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:326 350 | } 351 | 352 | func TestSliceSliceOutOfBounds(t *testing.T) { 353 | tests := []struct { 354 | start, stop int 355 | msg string 356 | }{ 357 | {-1, 3, "Invalid slice index"}, 358 | {0, 4, "Slice bounds out of range"}, 359 | {3, 2, "Invalid slice index"}, 360 | } 361 | 362 | for _, s := range tests { 363 | t.Run(fmt.Sprintf("start=%d, stop=%d", s.start, s.stop), func(t *testing.T) { 364 | defer assertPanic(t, s.msg) 365 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:340 366 | instantiate୦୦NewVector୦int(inputSlice(0, 10)...).Slice(2, 5).Slice(s.start, s.stop) 367 | //line /home/tobias/Development/go/peds/go2go/src/peds/vector_test.go2:342 368 | }) 369 | } 370 | } 371 | 372 | func TestToNativeVector(t *testing.T) { 373 | lengths := []int{0, 1, 7, 32, 512, 1000} 374 | for _, length := range lengths { 375 | t.Run(fmt.Sprintf("length=%d", length), func(t *testing.T) { 376 | inputS := inputSlice(0, length) 377 | v := instantiate୦୦NewVector୦int(inputS...) 378 | 379 | outputS := v.ToNativeSlice() 380 | 381 | assertEqual(t, len(inputS), len(outputS)) 382 | for i := range outputS { 383 | assertEqual(t, inputS[i], outputS[i]) 384 | } 385 | }) 386 | } 387 | } 388 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:34 389 | func instantiate୦୦NewVector୦int(items ...int, 390 | 391 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:34 392 | ) *instantiate୦୦Vector୦int { 393 | 394 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:37 395 | tail := make([]int, 0) 396 | v := &instantiate୦୦Vector୦int{root: emptyCommonNode, shift: shiftSize, tail: tail} 397 | return v.Append(items...) 398 | } 399 | 400 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:238 401 | func instantiate୦୦NewVectorSlice୦int(items ...(int),) *instantiate୦୦VectorSlice୦int { 402 | return &instantiate୦୦VectorSlice୦int{vector: instantiate୦୦NewVector୦int(items...), start: 0, stop: len(items)} 403 | } 404 | 405 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:240 406 | type instantiate୦୦Vector୦int struct { 407 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:27 408 | tail []int 409 | 410 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:28 411 | root commonNode 412 | len uint 413 | shift uint 414 | } 415 | 416 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:43 417 | func (v *instantiate୦୦Vector୦int,) Append(item ...int, 418 | 419 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:43 420 | ) *instantiate୦୦Vector୦int { 421 | result := v 422 | itemLen := uint(len(item)) 423 | for insertOffset := uint(0); insertOffset < itemLen; { 424 | tailLen := result.len - result.tailOffset() 425 | tailFree := nodeSize - tailLen 426 | if tailFree == 0 { 427 | result = result.pushLeafNode(result.tail) 428 | result.tail = make([]int, 0) 429 | tailFree = nodeSize 430 | tailLen = 0 431 | } 432 | 433 | batchLen := uintMin(itemLen-insertOffset, tailFree) 434 | newTail := make([]int, 0, tailLen+batchLen) 435 | newTail = append(newTail, result.tail...) 436 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 437 | result = &instantiate୦୦Vector୦int{root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 438 | insertOffset += batchLen 439 | } 440 | 441 | return result 442 | } 443 | 444 | func (v *instantiate୦୦Vector୦int,) tailOffset() uint { 445 | if v.len < nodeSize { 446 | return 0 447 | } 448 | 449 | return ((v.len - 1) >> shiftSize) << shiftSize 450 | } 451 | 452 | func (v *instantiate୦୦Vector୦int,) pushLeafNode(node []int, 453 | 454 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:75 455 | ) *instantiate୦୦Vector୦int { 456 | var newRoot commonNode 457 | newShift := v.shift 458 | 459 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:80 460 | if (v.len >> shiftSize) > (1 << v.shift) { 461 | newNode := newPath(v.shift, node) 462 | newRoot = commonNode([]commonNode{v.root, newNode}) 463 | newShift = v.shift + shiftSize 464 | } else { 465 | newRoot = v.pushTail(v.shift, v.root, node) 466 | } 467 | 468 | return &instantiate୦୦Vector୦int{root: newRoot, tail: v.tail, len: v.len, shift: newShift} 469 | } 470 | 471 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:99 472 | func (v *instantiate୦୦Vector୦int,) pushTail(level uint, parent commonNode, tailNode []int, 473 | 474 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:99 475 | ) commonNode { 476 | subIdx := ((v.len - 1) >> level) & shiftBitMask 477 | parentNode := parent.([]commonNode) 478 | ret := make([]commonNode, subIdx+1) 479 | copy(ret, parentNode) 480 | var nodeToInsert commonNode 481 | 482 | if level == shiftSize { 483 | nodeToInsert = tailNode 484 | } else if subIdx < uint(len(parentNode)) { 485 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 486 | } else { 487 | nodeToInsert = newPath(level-shiftSize, tailNode) 488 | } 489 | 490 | ret[subIdx] = nodeToInsert 491 | return ret 492 | } 493 | 494 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:119 495 | func (v *instantiate୦୦Vector୦int,) Len() int { 496 | return int(v.len) 497 | } 498 | 499 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:124 500 | func (v *instantiate୦୦Vector୦int,) Get(i int) int { 501 | if i < 0 || uint(i) >= v.len { 502 | panic("Index out of bounds") 503 | } 504 | 505 | return v.sliceFor(uint(i))[i&shiftBitMask] 506 | } 507 | 508 | func (v *instantiate୦୦Vector୦int,) sliceFor(i uint) []int { 509 | if i >= v.tailOffset() { 510 | return v.tail 511 | } 512 | 513 | node := v.root 514 | for level := v.shift; level > 0; level -= shiftSize { 515 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 516 | } 517 | 518 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:144 519 | return node.([]int) 520 | } 521 | 522 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:149 523 | func (v *instantiate୦୦Vector୦int,) Set(i int, item int, 524 | 525 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:149 526 | ) *instantiate୦୦Vector୦int { 527 | if i < 0 || uint(i) >= v.len { 528 | panic("Index out of bounds") 529 | } 530 | 531 | if uint(i) >= v.tailOffset() { 532 | newTail := make([]int, len(v.tail)) 533 | copy(newTail, v.tail) 534 | newTail[i&shiftBitMask] = item 535 | return &instantiate୦୦Vector୦int{root: v.root, tail: newTail, len: v.len, shift: v.shift} 536 | } 537 | 538 | return &instantiate୦୦Vector୦int{root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 539 | } 540 | 541 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:165 542 | func (v *instantiate୦୦Vector୦int,) doAssoc(level uint, node commonNode, i uint, item int, 543 | 544 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:165 545 | ) commonNode { 546 | if level == 0 { 547 | ret := make([]int, nodeSize) 548 | copy(ret, node.([]int)) 549 | ret[i&shiftBitMask] = item 550 | return ret 551 | } 552 | 553 | ret := make([]commonNode, nodeSize) 554 | copy(ret, node.([]commonNode)) 555 | subidx := (i >> level) & shiftBitMask 556 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 557 | return ret 558 | } 559 | 560 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:182 561 | func (v *instantiate୦୦Vector୦int,) Range(f func(int, 562 | 563 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:182 564 | ) bool) { 565 | var currentNode []int 566 | 567 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:184 568 | for i := uint(0); i < v.len; i++ { 569 | if i&shiftBitMask == 0 { 570 | currentNode = v.sliceFor(i) 571 | } 572 | 573 | if !f(currentNode[i&shiftBitMask]) { 574 | return 575 | } 576 | } 577 | } 578 | 579 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:196 580 | func (v *instantiate୦୦Vector୦int,) Slice(start, stop int) *instantiate୦୦VectorSlice୦int { 581 | assertSliceOk(start, stop, v.Len()) 582 | return &instantiate୦୦VectorSlice୦int{vector: v, start: start, stop: stop} 583 | } 584 | 585 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:203 586 | func (v *instantiate୦୦Vector୦int,) ToNativeSlice() []int { 587 | result := make([]int, 0, v.len) 588 | for i := uint(0); i < v.len; i += nodeSize { 589 | result = append(result, v.sliceFor(i)...) 590 | } 591 | 592 | return result 593 | } 594 | 595 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:210 596 | type instantiate୦୦VectorSlice୦int struct { 597 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:233 598 | vector *instantiate୦୦Vector୦int 599 | start, stop int 600 | } 601 | 602 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:243 603 | func (s *instantiate୦୦VectorSlice୦int,) Len() int { 604 | return s.stop - s.start 605 | } 606 | 607 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:248 608 | func (s *instantiate୦୦VectorSlice୦int,) Get(i int) (int) { 609 | if i < 0 || s.start+i >= s.stop { 610 | panic("Index out of bounds") 611 | } 612 | 613 | return s.vector.Get(s.start + i) 614 | } 615 | 616 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:257 617 | func (s *instantiate୦୦VectorSlice୦int,) Set(i int, item (int),) *instantiate୦୦VectorSlice୦int { 618 | if i < 0 || s.start+i >= s.stop { 619 | panic("Index out of bounds") 620 | } 621 | 622 | return s.vector.Set(s.start+i, item).Slice(s.start, s.stop) 623 | } 624 | 625 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:266 626 | func (s *instantiate୦୦VectorSlice୦int,) Append(items ...(int),) *instantiate୦୦VectorSlice୦int { 627 | newSlice := instantiate୦୦VectorSlice୦int{vector: s.vector, start: s.start, stop: s.stop + len(items)} 628 | 629 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:272 630 | itemPos := 0 631 | for ; s.stop+itemPos < s.vector.Len() && itemPos < len(items); itemPos++ { 632 | newSlice.vector = newSlice.vector.Set(s.stop+itemPos, items[itemPos]) 633 | } 634 | 635 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:278 636 | newSlice.vector = newSlice.vector.Append(items[itemPos:]...) 637 | return &newSlice 638 | } 639 | 640 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:283 641 | func (s *instantiate୦୦VectorSlice୦int,) Slice(start, stop int) *instantiate୦୦VectorSlice୦int { 642 | assertSliceOk(start, stop, s.stop-s.start) 643 | return &instantiate୦୦VectorSlice୦int{vector: s.vector, start: s.start + start, stop: s.start + stop} 644 | } 645 | 646 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:290 647 | func (s *instantiate୦୦VectorSlice୦int,) Range(f func((int),) bool) { 648 | var currentNode [](int) 649 | for i := uint(s.start); i < uint(s.stop); i++ { 650 | if i&shiftBitMask == 0 || i == uint(s.start) { 651 | currentNode = s.vector.sliceFor(uint(i)) 652 | } 653 | 654 | if !f(currentNode[i&shiftBitMask]) { 655 | return 656 | } 657 | } 658 | } 659 | 660 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:301 661 | var _ = fmt.Errorf 662 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:301 663 | var _ = math.Abs 664 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:301 665 | var _ = runtime.BlockProfile 666 | 667 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:301 668 | type _ strings.Builder 669 | 670 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:301 671 | var _ = testing.AllocsPerRun 672 | 673 | //line /home/tobias/Development/go/peds/go2go/src/peds/containers.go2:301 674 | type _ unsafe.Pointer 675 | -------------------------------------------------------------------------------- /go2go/src/peds/vector_test.go2: -------------------------------------------------------------------------------- 1 | package peds 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | // TODO: Should not be needed but code generation seems a bit broken 11 | // type commonNode interface{} 12 | 13 | func TestCanBeInstantiated(t *testing.T) { 14 | v := NewVector(int)(1, 2, 3) 15 | v2 := v.Append(4) 16 | if v2.Len() != 4 { 17 | t.Errorf("Expected %d != Expected 4", v2.Len()) 18 | } 19 | 20 | for i := 0; i < 4; i++ { 21 | actual, expected := v2.Get(i), i+1 22 | if actual != expected { 23 | t.Errorf("Actual %d != Expected %d", actual, expected) 24 | } 25 | } 26 | } 27 | 28 | /////////////// 29 | /// Helpers /// 30 | /////////////// 31 | 32 | func assertEqual(t *testing.T, expected, actual int) { 33 | if expected != actual { 34 | _, file, line, _ := runtime.Caller(1) 35 | t.Errorf("Error %s, line %d. Expected: %d, actual: %d", file, line, expected, actual) 36 | } 37 | } 38 | 39 | func assertEqualString(t *testing.T, expected, actual string) { 40 | if expected != actual { 41 | _, file, line, _ := runtime.Caller(1) 42 | t.Errorf("Error %s, line %d. Expected: %v, actual: %v", file, line, expected, actual) 43 | } 44 | } 45 | 46 | func assertEqualBool(t *testing.T, expected, actual bool) { 47 | if expected != actual { 48 | _, file, line, _ := runtime.Caller(1) 49 | t.Errorf("Error %s, line %d. Expected: %v, actual: %v", file, line, expected, actual) 50 | } 51 | } 52 | 53 | func assertPanic(t *testing.T, expectedMsg string) { 54 | if r := recover(); r == nil { 55 | _, _, line, _ := runtime.Caller(1) 56 | t.Errorf("Did not raise, line %d.", line) 57 | } else { 58 | msg := r.(string) 59 | if !strings.Contains(msg, expectedMsg) { 60 | t.Errorf("Msg '%s', did not contain '%s'", msg, expectedMsg) 61 | } 62 | } 63 | } 64 | 65 | func inputSlice(start, size int) []int { 66 | result := make([]int, 0, size) 67 | for i := start; i < start+size; i++ { 68 | result = append(result, i) 69 | } 70 | 71 | return result 72 | } 73 | 74 | var testSizes = []int{0, 1, 20, 32, 33, 50, 500, 32 * 32, 32*32 + 1, 10000, 32 * 32 * 32, 32*32*32 + 1} 75 | 76 | ////////////// 77 | /// Vector /// 78 | ////////////// 79 | 80 | func TestPropertiesOfNewVector(t *testing.T) { 81 | for _, l := range testSizes { 82 | t.Run(fmt.Sprintf("NewVector %d", l), func(t *testing.T) { 83 | vec := NewVector(inputSlice(0, l)...) 84 | assertEqual(t, vec.Len(), l) 85 | for i := 0; i < l; i++ { 86 | assertEqual(t, i, vec.Get(i)) 87 | } 88 | }) 89 | } 90 | } 91 | 92 | func TestSetItem(t *testing.T) { 93 | for _, l := range testSizes { 94 | t.Run(fmt.Sprintf("Set %d", l), func(t *testing.T) { 95 | vec := NewVector(inputSlice(0, l)...) 96 | for i := 0; i < l; i++ { 97 | newArr := vec.Set(i, -i) 98 | assertEqual(t, -i, newArr.Get(i)) 99 | assertEqual(t, i, vec.Get(i)) 100 | } 101 | }) 102 | } 103 | } 104 | 105 | func TestAppend(t *testing.T) { 106 | for _, l := range testSizes { 107 | vec := NewVector(inputSlice(0, l)...) 108 | t.Run(fmt.Sprintf("Append %d", l), func(t *testing.T) { 109 | for i := 0; i < 70; i++ { 110 | newVec := vec.Append(inputSlice(l, i)...) 111 | assertEqual(t, i+l, newVec.Len()) 112 | for j := 0; j < i+l; j++ { 113 | assertEqual(t, j, newVec.Get(j)) 114 | } 115 | 116 | // Original vector is unchanged 117 | assertEqual(t, l, vec.Len()) 118 | } 119 | }) 120 | } 121 | } 122 | 123 | func TestVectorSetOutOfBoundsNegative(t *testing.T) { 124 | defer assertPanic(t, "Index out of bounds") 125 | NewVector(inputSlice(0, 10)...).Set(-1, 0) 126 | } 127 | 128 | func TestVectorSetOutOfBoundsBeyondEnd(t *testing.T) { 129 | defer assertPanic(t, "Index out of bounds") 130 | NewVector(inputSlice(0, 10)...).Set(10, 0) 131 | } 132 | 133 | func TestVectorGetOutOfBoundsNegative(t *testing.T) { 134 | defer assertPanic(t, "Index out of bounds") 135 | NewVector(inputSlice(0, 10)...).Get(-1) 136 | } 137 | 138 | func TestVectorGetOutOfBoundsBeyondEnd(t *testing.T) { 139 | defer assertPanic(t, "Index out of bounds") 140 | NewVector(inputSlice(0, 10)...).Get(10) 141 | } 142 | 143 | func TestVectorSliceOutOfBounds(t *testing.T) { 144 | tests := []struct { 145 | start, stop int 146 | msg string 147 | }{ 148 | {-1, 3, "Invalid slice index"}, 149 | {0, 11, "Slice bounds out of range"}, 150 | {5, 3, "Invalid slice index"}, 151 | } 152 | 153 | for _, s := range tests { 154 | t.Run(fmt.Sprintf("start=%d, stop=%d", s.start, s.stop), func(t *testing.T) { 155 | defer assertPanic(t, s.msg) 156 | NewVector(inputSlice(0, 10)...).Slice(s.start, s.stop) 157 | }) 158 | } 159 | } 160 | 161 | func TestCompleteIteration(t *testing.T) { 162 | input := inputSlice(0, 10000) 163 | dst := make([]int, 0, 10000) 164 | NewVector(input...).Range(func(elem int) bool { 165 | dst = append(dst, elem) 166 | return true 167 | }) 168 | 169 | assertEqual(t, len(input), len(dst)) 170 | assertEqual(t, input[0], dst[0]) 171 | assertEqual(t, input[5000], dst[5000]) 172 | assertEqual(t, input[9999], dst[9999]) 173 | } 174 | 175 | func TestCanceledIteration(t *testing.T) { 176 | input := inputSlice(0, 10000) 177 | count := 0 178 | NewVector(input...).Range(func(elem int) bool { 179 | count++ 180 | if count == 5 { 181 | return false 182 | } 183 | return true 184 | }) 185 | 186 | assertEqual(t, 5, count) 187 | } 188 | 189 | ///////////// 190 | /// Slice /// 191 | ///////////// 192 | 193 | func TestSliceIndexes(t *testing.T) { 194 | vec := NewVector(inputSlice(0, 1000)...) 195 | slice := vec.Slice(0, 10) 196 | assertEqual(t, 1000, vec.Len()) 197 | assertEqual(t, 10, slice.Len()) 198 | assertEqual(t, 0, slice.Get(0)) 199 | assertEqual(t, 9, slice.Get(9)) 200 | 201 | // Slice of slice 202 | slice2 := slice.Slice(3, 7) 203 | assertEqual(t, 10, slice.Len()) 204 | assertEqual(t, 4, slice2.Len()) 205 | assertEqual(t, 3, slice2.Get(0)) 206 | assertEqual(t, 6, slice2.Get(3)) 207 | } 208 | 209 | func TestSliceCreation(t *testing.T) { 210 | sliceLen := 10000 211 | slice := NewVectorSlice(inputSlice(0, sliceLen)...) 212 | assertEqual(t, slice.Len(), sliceLen) 213 | for i := 0; i < sliceLen; i++ { 214 | assertEqual(t, i, slice.Get(i)) 215 | } 216 | } 217 | 218 | func TestSliceSet(t *testing.T) { 219 | vector := NewVector(inputSlice(0, 1000)...) 220 | slice := vector.Slice(10, 100) 221 | slice2 := slice.Set(5, 123) 222 | 223 | // Underlying vector and original slice should remain unchanged. New slice updated 224 | // in the correct position 225 | assertEqual(t, 15, vector.Get(15)) 226 | assertEqual(t, 15, slice.Get(5)) 227 | assertEqual(t, 123, slice2.Get(5)) 228 | } 229 | 230 | func TestSliceAppendInTheMiddleOfBackingVector(t *testing.T) { 231 | vector := NewVector(inputSlice(0, 100)...) 232 | slice := vector.Slice(0, 50) 233 | slice2 := slice.Append(inputSlice(0, 10)...) 234 | 235 | // New slice 236 | assertEqual(t, 60, slice2.Len()) 237 | assertEqual(t, 0, slice2.Get(50)) 238 | assertEqual(t, 9, slice2.Get(59)) 239 | 240 | // Original slice 241 | assertEqual(t, 50, slice.Len()) 242 | 243 | // Original vector 244 | assertEqual(t, 100, vector.Len()) 245 | assertEqual(t, 50, vector.Get(50)) 246 | assertEqual(t, 59, vector.Get(59)) 247 | } 248 | 249 | func TestSliceAppendAtTheEndOfBackingVector(t *testing.T) { 250 | vector := NewVector(inputSlice(0, 100)...) 251 | slice := vector.Slice(0, 100) 252 | slice2 := slice.Append(inputSlice(0, 10)...) 253 | 254 | // New slice 255 | assertEqual(t, 110, slice2.Len()) 256 | assertEqual(t, 0, slice2.Get(100)) 257 | assertEqual(t, 9, slice2.Get(109)) 258 | 259 | // Original slice 260 | assertEqual(t, 100, slice.Len()) 261 | } 262 | 263 | func TestSliceAppendAtMiddleToEndOfBackingVector(t *testing.T) { 264 | vector := NewVector(inputSlice(0, 100)...) 265 | slice := vector.Slice(0, 50) 266 | slice2 := slice.Append(inputSlice(0, 100)...) 267 | 268 | // New slice 269 | assertEqual(t, 150, slice2.Len()) 270 | assertEqual(t, 0, slice2.Get(50)) 271 | assertEqual(t, 99, slice2.Get(149)) 272 | 273 | // Original slice 274 | assertEqual(t, 50, slice.Len()) 275 | } 276 | 277 | func TestSliceCompleteIteration(t *testing.T) { 278 | vec := NewVector(inputSlice(0, 1000)...) 279 | dst := make([]int, 0) 280 | 281 | vec.Slice(5, 200).Range(func(elem int) bool { 282 | dst = append(dst, elem) 283 | return true 284 | }) 285 | 286 | assertEqual(t, 195, len(dst)) 287 | assertEqual(t, 5, dst[0]) 288 | assertEqual(t, 55, dst[50]) 289 | assertEqual(t, 199, dst[194]) 290 | } 291 | 292 | func TestSliceCanceledIteration(t *testing.T) { 293 | vec := NewVector(inputSlice(0, 1000)...) 294 | count := 0 295 | 296 | vec.Slice(5, 200).Range(func(elem int) bool { 297 | count++ 298 | if count == 5 { 299 | return false 300 | } 301 | 302 | return true 303 | }) 304 | 305 | assertEqual(t, 5, count) 306 | } 307 | 308 | func TestSliceSetOutOfBoundsNegative(t *testing.T) { 309 | defer assertPanic(t, "Index out of bounds") 310 | NewVector(inputSlice(0, 10)...).Slice(2, 5).Set(-1, 0) 311 | } 312 | 313 | func TestSliceSetOutOfBoundsBeyondEnd(t *testing.T) { 314 | defer assertPanic(t, "Index out of bounds") 315 | NewVector(inputSlice(0, 10)...).Slice(2, 5).Set(4, 0) 316 | } 317 | 318 | func TestSliceGetOutOfBoundsNegative(t *testing.T) { 319 | defer assertPanic(t, "Index out of bounds") 320 | NewVector(inputSlice(0, 10)...).Slice(2, 5).Get(-1) 321 | } 322 | 323 | func TestSliceGetOutOfBoundsBeyondEnd(t *testing.T) { 324 | defer assertPanic(t, "Index out of bounds") 325 | NewVector(inputSlice(0, 10)...).Slice(2, 5).Get(4) 326 | } 327 | 328 | func TestSliceSliceOutOfBounds(t *testing.T) { 329 | tests := []struct { 330 | start, stop int 331 | msg string 332 | }{ 333 | {-1, 3, "Invalid slice index"}, 334 | {0, 4, "Slice bounds out of range"}, 335 | {3, 2, "Invalid slice index"}, 336 | } 337 | 338 | for _, s := range tests { 339 | t.Run(fmt.Sprintf("start=%d, stop=%d", s.start, s.stop), func(t *testing.T) { 340 | defer assertPanic(t, s.msg) 341 | NewVector(inputSlice(0, 10)...).Slice(2, 5).Slice(s.start, s.stop) 342 | }) 343 | } 344 | } 345 | 346 | func TestToNativeVector(t *testing.T) { 347 | lengths := []int{0, 1, 7, 32, 512, 1000} 348 | for _, length := range lengths { 349 | t.Run(fmt.Sprintf("length=%d", length), func(t *testing.T) { 350 | inputS := inputSlice(0, length) 351 | v := NewVector(inputS...) 352 | 353 | outputS := v.ToNativeSlice() 354 | 355 | assertEqual(t, len(inputS), len(outputS)) 356 | for i := range outputS { 357 | assertEqual(t, inputS[i], outputS[i]) 358 | } 359 | }) 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /internal/generic_types/containers.go: -------------------------------------------------------------------------------- 1 | // Package generic_types contains underlying Go implementation of types that 2 | // will be used in text templates to generate specific implementations. 3 | package generic_types 4 | 5 | //template:CommonTemplate 6 | 7 | import ( 8 | "encoding/binary" 9 | "fmt" 10 | "hash/crc32" 11 | "math" 12 | "unsafe" 13 | ) 14 | 15 | const shiftSize = 5 16 | const nodeSize = 32 17 | const shiftBitMask = 0x1F 18 | 19 | type commonNode interface{} 20 | 21 | var emptyCommonNode commonNode = []commonNode{} 22 | 23 | func uintMin(a, b uint) uint { 24 | if a < b { 25 | return a 26 | } 27 | 28 | return b 29 | } 30 | 31 | func newPath(shift uint, node commonNode) commonNode { 32 | if shift == 0 { 33 | return node 34 | } 35 | 36 | return newPath(shift-shiftSize, commonNode([]commonNode{node})) 37 | } 38 | 39 | func assertSliceOk(start, stop, len int) { 40 | if start < 0 { 41 | panic(fmt.Sprintf("Invalid slice index %d (index must be non-negative)", start)) 42 | } 43 | 44 | if start > stop { 45 | panic(fmt.Sprintf("Invalid slice index: %d > %d", start, stop)) 46 | } 47 | 48 | if stop > len { 49 | panic(fmt.Sprintf("Slice bounds out of range, start=%d, stop=%d, len=%d", start, stop, len)) 50 | } 51 | } 52 | 53 | const upperMapLoadFactor float64 = 8.0 54 | const lowerMapLoadFactor float64 = 2.0 55 | const initialMapLoadFactor float64 = (upperMapLoadFactor + lowerMapLoadFactor) / 2 56 | 57 | ////////////////////////// 58 | //// Hash functions ////// 59 | ////////////////////////// 60 | 61 | func hash(x []byte) uint32 { 62 | return crc32.ChecksumIEEE(x) 63 | } 64 | 65 | //go:noescape 66 | //go:linkname nilinterhash runtime.nilinterhash 67 | func nilinterhash(p unsafe.Pointer, h uintptr) uintptr 68 | 69 | func interfaceHash(x interface{}) uint32 { 70 | return uint32(nilinterhash(unsafe.Pointer(&x), 0)) 71 | } 72 | 73 | func byteHash(x byte) uint32 { 74 | return hash([]byte{x}) 75 | } 76 | 77 | func uint8Hash(x uint8) uint32 { 78 | return byteHash(byte(x)) 79 | } 80 | 81 | func int8Hash(x int8) uint32 { 82 | return uint8Hash(uint8(x)) 83 | } 84 | 85 | func uint16Hash(x uint16) uint32 { 86 | bX := make([]byte, 2) 87 | binary.LittleEndian.PutUint16(bX, x) 88 | return hash(bX) 89 | } 90 | 91 | func int16Hash(x int16) uint32 { 92 | return uint16Hash(uint16(x)) 93 | } 94 | 95 | func uint32Hash(x uint32) uint32 { 96 | bX := make([]byte, 4) 97 | binary.LittleEndian.PutUint32(bX, x) 98 | return hash(bX) 99 | } 100 | 101 | func int32Hash(x int32) uint32 { 102 | return uint32Hash(uint32(x)) 103 | } 104 | 105 | func uint64Hash(x uint64) uint32 { 106 | bX := make([]byte, 8) 107 | binary.LittleEndian.PutUint64(bX, x) 108 | return hash(bX) 109 | } 110 | 111 | func int64Hash(x int64) uint32 { 112 | return uint64Hash(uint64(x)) 113 | } 114 | 115 | func intHash(x int) uint32 { 116 | return int64Hash(int64(x)) 117 | } 118 | 119 | func uintHash(x uint) uint32 { 120 | return uint64Hash(uint64(x)) 121 | } 122 | 123 | func boolHash(x bool) uint32 { 124 | if x { 125 | return 1 126 | } 127 | 128 | return 0 129 | } 130 | 131 | func runeHash(x rune) uint32 { 132 | return int32Hash(int32(x)) 133 | } 134 | 135 | func stringHash(x string) uint32 { 136 | return hash([]byte(x)) 137 | } 138 | 139 | func float64Hash(x float64) uint32 { 140 | return uint64Hash(math.Float64bits(x)) 141 | } 142 | 143 | func float32Hash(x float32) uint32 { 144 | return uint32Hash(math.Float32bits(x)) 145 | } 146 | 147 | //template:VectorTemplate 148 | 149 | ////////////// 150 | /// Vector /// 151 | ////////////// 152 | 153 | // A GenericVectorType is an ordered persistent/immutable collection of items corresponding roughly 154 | // to the use cases for a slice. 155 | type GenericVectorType struct { 156 | tail []GenericType 157 | root commonNode 158 | len uint 159 | shift uint 160 | } 161 | 162 | var emptyGenericVectorTypeTail = make([]GenericType, 0) 163 | var emptyGenericVectorType *GenericVectorType = &GenericVectorType{root: emptyCommonNode, shift: shiftSize, tail: emptyGenericVectorTypeTail} 164 | 165 | // NewGenericVectorType returns a new GenericVectorType containing the items provided in items. 166 | func NewGenericVectorType(items ...GenericType) *GenericVectorType { 167 | return emptyGenericVectorType.Append(items...) 168 | } 169 | 170 | // Get returns the element at position i. 171 | func (v *GenericVectorType) Get(i int) GenericType { 172 | if i < 0 || uint(i) >= v.len { 173 | panic("Index out of bounds") 174 | } 175 | 176 | return v.sliceFor(uint(i))[i&shiftBitMask] 177 | } 178 | 179 | func (v *GenericVectorType) sliceFor(i uint) []GenericType { 180 | if i >= v.tailOffset() { 181 | return v.tail 182 | } 183 | 184 | node := v.root 185 | for level := v.shift; level > 0; level -= shiftSize { 186 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 187 | } 188 | 189 | return node.([]GenericType) 190 | } 191 | 192 | func (v *GenericVectorType) tailOffset() uint { 193 | if v.len < nodeSize { 194 | return 0 195 | } 196 | 197 | return ((v.len - 1) >> shiftSize) << shiftSize 198 | } 199 | 200 | // Set returns a new vector with the element at position i set to item. 201 | func (v *GenericVectorType) Set(i int, item GenericType) *GenericVectorType { 202 | if i < 0 || uint(i) >= v.len { 203 | panic("Index out of bounds") 204 | } 205 | 206 | if uint(i) >= v.tailOffset() { 207 | newTail := make([]GenericType, len(v.tail)) 208 | copy(newTail, v.tail) 209 | newTail[i&shiftBitMask] = item 210 | return &GenericVectorType{root: v.root, tail: newTail, len: v.len, shift: v.shift} 211 | } 212 | 213 | return &GenericVectorType{root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 214 | } 215 | 216 | func (v *GenericVectorType) doAssoc(level uint, node commonNode, i uint, item GenericType) commonNode { 217 | if level == 0 { 218 | ret := make([]GenericType, nodeSize) 219 | copy(ret, node.([]GenericType)) 220 | ret[i&shiftBitMask] = item 221 | return ret 222 | } 223 | 224 | ret := make([]commonNode, nodeSize) 225 | copy(ret, node.([]commonNode)) 226 | subidx := (i >> level) & shiftBitMask 227 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 228 | return ret 229 | } 230 | 231 | func (v *GenericVectorType) pushTail(level uint, parent commonNode, tailNode []GenericType) commonNode { 232 | subIdx := ((v.len - 1) >> level) & shiftBitMask 233 | parentNode := parent.([]commonNode) 234 | ret := make([]commonNode, subIdx+1) 235 | copy(ret, parentNode) 236 | var nodeToInsert commonNode 237 | 238 | if level == shiftSize { 239 | nodeToInsert = tailNode 240 | } else if subIdx < uint(len(parentNode)) { 241 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 242 | } else { 243 | nodeToInsert = newPath(level-shiftSize, tailNode) 244 | } 245 | 246 | ret[subIdx] = nodeToInsert 247 | return ret 248 | } 249 | 250 | // Append returns a new vector with item(s) appended to it. 251 | func (v *GenericVectorType) Append(item ...GenericType) *GenericVectorType { 252 | result := v 253 | itemLen := uint(len(item)) 254 | for insertOffset := uint(0); insertOffset < itemLen; { 255 | tailLen := result.len - result.tailOffset() 256 | tailFree := nodeSize - tailLen 257 | if tailFree == 0 { 258 | result = result.pushLeafNode(result.tail) 259 | result.tail = emptyGenericVectorType.tail 260 | tailFree = nodeSize 261 | tailLen = 0 262 | } 263 | 264 | batchLen := uintMin(itemLen-insertOffset, tailFree) 265 | newTail := make([]GenericType, 0, tailLen+batchLen) 266 | newTail = append(newTail, result.tail...) 267 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 268 | result = &GenericVectorType{root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 269 | insertOffset += batchLen 270 | } 271 | 272 | return result 273 | } 274 | 275 | func (v *GenericVectorType) pushLeafNode(node []GenericType) *GenericVectorType { 276 | var newRoot commonNode 277 | newShift := v.shift 278 | 279 | // Root overflow? 280 | if (v.len >> shiftSize) > (1 << v.shift) { 281 | newNode := newPath(v.shift, node) 282 | newRoot = commonNode([]commonNode{v.root, newNode}) 283 | newShift = v.shift + shiftSize 284 | } else { 285 | newRoot = v.pushTail(v.shift, v.root, node) 286 | } 287 | 288 | return &GenericVectorType{root: newRoot, tail: v.tail, len: v.len, shift: newShift} 289 | } 290 | 291 | // Slice returns a GenericVectorTypeSlice that refers to all elements [start,stop) in v. 292 | func (v *GenericVectorType) Slice(start, stop int) *GenericVectorTypeSlice { 293 | assertSliceOk(start, stop, v.Len()) 294 | return &GenericVectorTypeSlice{vector: v, start: start, stop: stop} 295 | } 296 | 297 | // Len returns the length of v. 298 | func (v *GenericVectorType) Len() int { 299 | return int(v.len) 300 | } 301 | 302 | // Range calls f repeatedly passing it each element in v in order as argument until either 303 | // all elements have been visited or f returns false. 304 | func (v *GenericVectorType) Range(f func(GenericType) bool) { 305 | var currentNode []GenericType 306 | for i := uint(0); i < v.len; i++ { 307 | if i&shiftBitMask == 0 { 308 | currentNode = v.sliceFor(i) 309 | } 310 | 311 | if !f(currentNode[i&shiftBitMask]) { 312 | return 313 | } 314 | } 315 | } 316 | 317 | // ToNativeSlice returns a Go slice containing all elements of v 318 | func (v *GenericVectorType) ToNativeSlice() []GenericType { 319 | result := make([]GenericType, 0, v.len) 320 | for i := uint(0); i < v.len; i += nodeSize { 321 | result = append(result, v.sliceFor(i)...) 322 | } 323 | 324 | return result 325 | } 326 | 327 | //template:SliceTemplate 328 | 329 | //////////////// 330 | //// Slice ///// 331 | //////////////// 332 | 333 | // GenericVectorTypeSlice is a slice type backed by a GenericVectorType. 334 | type GenericVectorTypeSlice struct { 335 | vector *GenericVectorType 336 | start, stop int 337 | } 338 | 339 | // NewGenericVectorTypeSlice returns a new NewGenericVectorTypeSlice containing the items provided in items. 340 | func NewGenericVectorTypeSlice(items ...GenericType) *GenericVectorTypeSlice { 341 | return &GenericVectorTypeSlice{vector: emptyGenericVectorType.Append(items...), start: 0, stop: len(items)} 342 | } 343 | 344 | // Len returns the length of s. 345 | func (s *GenericVectorTypeSlice) Len() int { 346 | return s.stop - s.start 347 | } 348 | 349 | // Get returns the element at position i. 350 | func (s *GenericVectorTypeSlice) Get(i int) GenericType { 351 | if i < 0 || s.start+i >= s.stop { 352 | panic("Index out of bounds") 353 | } 354 | 355 | return s.vector.Get(s.start + i) 356 | } 357 | 358 | // Set returns a new slice with the element at position i set to item. 359 | func (s *GenericVectorTypeSlice) Set(i int, item GenericType) *GenericVectorTypeSlice { 360 | if i < 0 || s.start+i >= s.stop { 361 | panic("Index out of bounds") 362 | } 363 | 364 | return s.vector.Set(s.start+i, item).Slice(s.start, s.stop) 365 | } 366 | 367 | // Append returns a new slice with item(s) appended to it. 368 | func (s *GenericVectorTypeSlice) Append(items ...GenericType) *GenericVectorTypeSlice { 369 | newSlice := GenericVectorTypeSlice{vector: s.vector, start: s.start, stop: s.stop + len(items)} 370 | 371 | // If this is v slice that has an upper bound that is lower than the backing 372 | // vector then set the values in the backing vector to achieve some structural 373 | // sharing. 374 | itemPos := 0 375 | for ; s.stop+itemPos < s.vector.Len() && itemPos < len(items); itemPos++ { 376 | newSlice.vector = newSlice.vector.Set(s.stop+itemPos, items[itemPos]) 377 | } 378 | 379 | // For the rest just append it to the underlying vector 380 | newSlice.vector = newSlice.vector.Append(items[itemPos:]...) 381 | return &newSlice 382 | } 383 | 384 | // Slice returns a GenericVectorTypeSlice that refers to all elements [start,stop) in s. 385 | func (s *GenericVectorTypeSlice) Slice(start, stop int) *GenericVectorTypeSlice { 386 | assertSliceOk(start, stop, s.stop-s.start) 387 | return &GenericVectorTypeSlice{vector: s.vector, start: s.start + start, stop: s.start + stop} 388 | } 389 | 390 | // Range calls f repeatedly passing it each element in s in order as argument until either 391 | // all elements have been visited or f returns false. 392 | func (s *GenericVectorTypeSlice) Range(f func(GenericType) bool) { 393 | var currentNode []GenericType 394 | for i := uint(s.start); i < uint(s.stop); i++ { 395 | if i&shiftBitMask == 0 || i == uint(s.start) { 396 | currentNode = s.vector.sliceFor(uint(i)) 397 | } 398 | 399 | if !f(currentNode[i&shiftBitMask]) { 400 | return 401 | } 402 | } 403 | } 404 | 405 | //template:PrivateMapTemplate 406 | 407 | /////////// 408 | /// Map /// 409 | /////////// 410 | 411 | ////////////////////// 412 | /// Backing vector /// 413 | ////////////////////// 414 | 415 | type privateGenericMapItemBucketVector struct { 416 | tail []privateGenericMapItemBucket 417 | root commonNode 418 | len uint 419 | shift uint 420 | } 421 | 422 | type GenericMapItem struct { 423 | Key GenericMapKeyType 424 | Value GenericMapValueType 425 | } 426 | 427 | type privateGenericMapItemBucket []GenericMapItem 428 | 429 | var emptyGenericMapItemBucketVectorTail = make([]privateGenericMapItemBucket, 0) 430 | var emptyGenericMapItemBucketVector *privateGenericMapItemBucketVector = &privateGenericMapItemBucketVector{root: emptyCommonNode, shift: shiftSize, tail: emptyGenericMapItemBucketVectorTail} 431 | 432 | func (v *privateGenericMapItemBucketVector) Get(i int) privateGenericMapItemBucket { 433 | if i < 0 || uint(i) >= v.len { 434 | panic("Index out of bounds") 435 | } 436 | 437 | return v.sliceFor(uint(i))[i&shiftBitMask] 438 | } 439 | 440 | func (v *privateGenericMapItemBucketVector) sliceFor(i uint) []privateGenericMapItemBucket { 441 | if i >= v.tailOffset() { 442 | return v.tail 443 | } 444 | 445 | node := v.root 446 | for level := v.shift; level > 0; level -= shiftSize { 447 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 448 | } 449 | 450 | return node.([]privateGenericMapItemBucket) 451 | } 452 | 453 | func (v *privateGenericMapItemBucketVector) tailOffset() uint { 454 | if v.len < nodeSize { 455 | return 0 456 | } 457 | 458 | return ((v.len - 1) >> shiftSize) << shiftSize 459 | } 460 | 461 | func (v *privateGenericMapItemBucketVector) Set(i int, item privateGenericMapItemBucket) *privateGenericMapItemBucketVector { 462 | if i < 0 || uint(i) >= v.len { 463 | panic("Index out of bounds") 464 | } 465 | 466 | if uint(i) >= v.tailOffset() { 467 | newTail := make([]privateGenericMapItemBucket, len(v.tail)) 468 | copy(newTail, v.tail) 469 | newTail[i&shiftBitMask] = item 470 | return &privateGenericMapItemBucketVector{root: v.root, tail: newTail, len: v.len, shift: v.shift} 471 | } 472 | 473 | return &privateGenericMapItemBucketVector{root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 474 | } 475 | 476 | func (v *privateGenericMapItemBucketVector) doAssoc(level uint, node commonNode, i uint, item privateGenericMapItemBucket) commonNode { 477 | if level == 0 { 478 | ret := make([]privateGenericMapItemBucket, nodeSize) 479 | copy(ret, node.([]privateGenericMapItemBucket)) 480 | ret[i&shiftBitMask] = item 481 | return ret 482 | } 483 | 484 | ret := make([]commonNode, nodeSize) 485 | copy(ret, node.([]commonNode)) 486 | subidx := (i >> level) & shiftBitMask 487 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 488 | return ret 489 | } 490 | 491 | func (v *privateGenericMapItemBucketVector) pushTail(level uint, parent commonNode, tailNode []privateGenericMapItemBucket) commonNode { 492 | subIdx := ((v.len - 1) >> level) & shiftBitMask 493 | parentNode := parent.([]commonNode) 494 | ret := make([]commonNode, subIdx+1) 495 | copy(ret, parentNode) 496 | var nodeToInsert commonNode 497 | 498 | if level == shiftSize { 499 | nodeToInsert = tailNode 500 | } else if subIdx < uint(len(parentNode)) { 501 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 502 | } else { 503 | nodeToInsert = newPath(level-shiftSize, tailNode) 504 | } 505 | 506 | ret[subIdx] = nodeToInsert 507 | return ret 508 | } 509 | 510 | func (v *privateGenericMapItemBucketVector) Append(item ...privateGenericMapItemBucket) *privateGenericMapItemBucketVector { 511 | result := v 512 | itemLen := uint(len(item)) 513 | for insertOffset := uint(0); insertOffset < itemLen; { 514 | tailLen := result.len - result.tailOffset() 515 | tailFree := nodeSize - tailLen 516 | if tailFree == 0 { 517 | result = result.pushLeafNode(result.tail) 518 | result.tail = emptyGenericMapItemBucketVector.tail 519 | tailFree = nodeSize 520 | tailLen = 0 521 | } 522 | 523 | batchLen := uintMin(itemLen-insertOffset, tailFree) 524 | newTail := make([]privateGenericMapItemBucket, 0, tailLen+batchLen) 525 | newTail = append(newTail, result.tail...) 526 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 527 | result = &privateGenericMapItemBucketVector{root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 528 | insertOffset += batchLen 529 | } 530 | 531 | return result 532 | } 533 | 534 | func (v *privateGenericMapItemBucketVector) pushLeafNode(node []privateGenericMapItemBucket) *privateGenericMapItemBucketVector { 535 | var newRoot commonNode 536 | newShift := v.shift 537 | 538 | // Root overflow? 539 | if (v.len >> shiftSize) > (1 << v.shift) { 540 | newNode := newPath(v.shift, node) 541 | newRoot = commonNode([]commonNode{v.root, newNode}) 542 | newShift = v.shift + shiftSize 543 | } else { 544 | newRoot = v.pushTail(v.shift, v.root, node) 545 | } 546 | 547 | return &privateGenericMapItemBucketVector{root: newRoot, tail: v.tail, len: v.len, shift: newShift} 548 | } 549 | 550 | func (v *privateGenericMapItemBucketVector) Len() int { 551 | return int(v.len) 552 | } 553 | 554 | func (v *privateGenericMapItemBucketVector) Range(f func(privateGenericMapItemBucket) bool) { 555 | var currentNode []privateGenericMapItemBucket 556 | for i := uint(0); i < v.len; i++ { 557 | if i&shiftBitMask == 0 { 558 | currentNode = v.sliceFor(uint(i)) 559 | } 560 | 561 | if !f(currentNode[i&shiftBitMask]) { 562 | return 563 | } 564 | } 565 | } 566 | 567 | // GenericMapType is a persistent key - value map 568 | type GenericMapType struct { 569 | backingVector *privateGenericMapItemBucketVector 570 | len int 571 | } 572 | 573 | func (m *GenericMapType) pos(key GenericMapKeyType) int { 574 | return int(uint64(genericHash(key)) % uint64(m.backingVector.Len())) 575 | } 576 | 577 | // Helper type used during map creation and reallocation 578 | type privateGenericMapItemBuckets struct { 579 | buckets []privateGenericMapItemBucket 580 | length int 581 | } 582 | 583 | func newPrivateGenericMapItemBuckets(itemCount int) *privateGenericMapItemBuckets { 584 | size := int(float64(itemCount)/initialMapLoadFactor) + 1 585 | buckets := make([]privateGenericMapItemBucket, size) 586 | return &privateGenericMapItemBuckets{buckets: buckets} 587 | } 588 | 589 | func (b *privateGenericMapItemBuckets) AddItem(item GenericMapItem) { 590 | ix := int(uint64(genericHash(item.Key)) % uint64(len(b.buckets))) 591 | bucket := b.buckets[ix] 592 | if bucket != nil { 593 | // Hash collision, merge with existing bucket 594 | for keyIx, bItem := range bucket { 595 | if item.Key == bItem.Key { 596 | bucket[keyIx] = item 597 | return 598 | } 599 | } 600 | 601 | b.buckets[ix] = append(bucket, GenericMapItem{Key: item.Key, Value: item.Value}) 602 | b.length++ 603 | } else { 604 | bucket := make(privateGenericMapItemBucket, 0, int(math.Max(initialMapLoadFactor, 1.0))) 605 | b.buckets[ix] = append(bucket, item) 606 | b.length++ 607 | } 608 | } 609 | 610 | func (b *privateGenericMapItemBuckets) AddItemsFromMap(m *GenericMapType) { 611 | m.backingVector.Range(func(bucket privateGenericMapItemBucket) bool { 612 | for _, item := range bucket { 613 | b.AddItem(item) 614 | } 615 | return true 616 | }) 617 | } 618 | 619 | func newGenericMapType(items []GenericMapItem) *GenericMapType { 620 | buckets := newPrivateGenericMapItemBuckets(len(items)) 621 | for _, item := range items { 622 | buckets.AddItem(item) 623 | } 624 | 625 | return &GenericMapType{backingVector: emptyGenericMapItemBucketVector.Append(buckets.buckets...), len: buckets.length} 626 | } 627 | 628 | // Len returns the number of items in m. 629 | func (m *GenericMapType) Len() int { 630 | return int(m.len) 631 | } 632 | 633 | // Load returns value identified by key. ok is set to true if key exists in the map, false otherwise. 634 | func (m *GenericMapType) Load(key GenericMapKeyType) (value GenericMapValueType, ok bool) { 635 | bucket := m.backingVector.Get(m.pos(key)) 636 | if bucket != nil { 637 | for _, item := range bucket { 638 | if item.Key == key { 639 | return item.Value, true 640 | } 641 | } 642 | } 643 | 644 | var zeroValue GenericMapValueType 645 | return zeroValue, false 646 | } 647 | 648 | // Store returns a new GenericMapType containing value identified by key. 649 | func (m *GenericMapType) Store(key GenericMapKeyType, value GenericMapValueType) *GenericMapType { 650 | // Grow backing vector if load factor is too high 651 | if m.Len() >= m.backingVector.Len()*int(upperMapLoadFactor) { 652 | buckets := newPrivateGenericMapItemBuckets(m.Len() + 1) 653 | buckets.AddItemsFromMap(m) 654 | buckets.AddItem(GenericMapItem{Key: key, Value: value}) 655 | return &GenericMapType{backingVector: emptyGenericMapItemBucketVector.Append(buckets.buckets...), len: buckets.length} 656 | } 657 | 658 | pos := m.pos(key) 659 | bucket := m.backingVector.Get(pos) 660 | if bucket != nil { 661 | for ix, item := range bucket { 662 | if item.Key == key { 663 | // Overwrite existing item 664 | newBucket := make(privateGenericMapItemBucket, len(bucket)) 665 | copy(newBucket, bucket) 666 | newBucket[ix] = GenericMapItem{Key: key, Value: value} 667 | return &GenericMapType{backingVector: m.backingVector.Set(pos, newBucket), len: m.len} 668 | } 669 | } 670 | 671 | // Add new item to bucket 672 | newBucket := make(privateGenericMapItemBucket, len(bucket), len(bucket)+1) 673 | copy(newBucket, bucket) 674 | newBucket = append(newBucket, GenericMapItem{Key: key, Value: value}) 675 | return &GenericMapType{backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 676 | } 677 | 678 | item := GenericMapItem{Key: key, Value: value} 679 | newBucket := privateGenericMapItemBucket{item} 680 | return &GenericMapType{backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 681 | } 682 | 683 | // Delete returns a new GenericMapType without the element identified by key. 684 | func (m *GenericMapType) Delete(key GenericMapKeyType) *GenericMapType { 685 | pos := m.pos(key) 686 | bucket := m.backingVector.Get(pos) 687 | if bucket != nil { 688 | newBucket := make(privateGenericMapItemBucket, 0) 689 | for _, item := range bucket { 690 | if item.Key != key { 691 | newBucket = append(newBucket, item) 692 | } 693 | } 694 | 695 | removedItemCount := len(bucket) - len(newBucket) 696 | if removedItemCount == 0 { 697 | return m 698 | } 699 | 700 | if len(newBucket) == 0 { 701 | newBucket = nil 702 | } 703 | 704 | newMap := &GenericMapType{backingVector: m.backingVector.Set(pos, newBucket), len: m.len - removedItemCount} 705 | if newMap.backingVector.Len() > 1 && newMap.Len() < newMap.backingVector.Len()*int(lowerMapLoadFactor) { 706 | // Shrink backing vector if needed to avoid occupying excessive space 707 | buckets := newPrivateGenericMapItemBuckets(newMap.Len()) 708 | buckets.AddItemsFromMap(newMap) 709 | return &GenericMapType{backingVector: emptyGenericMapItemBucketVector.Append(buckets.buckets...), len: buckets.length} 710 | } 711 | 712 | return newMap 713 | } 714 | 715 | return m 716 | } 717 | 718 | // Range calls f repeatedly passing it each key and value as argument until either 719 | // all elements have been visited or f returns false. 720 | func (m *GenericMapType) Range(f func(GenericMapKeyType, GenericMapValueType) bool) { 721 | m.backingVector.Range(func(bucket privateGenericMapItemBucket) bool { 722 | for _, item := range bucket { 723 | if !f(item.Key, item.Value) { 724 | return false 725 | } 726 | } 727 | return true 728 | }) 729 | } 730 | 731 | // ToNativeMap returns a native Go map containing all elements of m. 732 | func (m *GenericMapType) ToNativeMap() map[GenericMapKeyType]GenericMapValueType { 733 | result := make(map[GenericMapKeyType]GenericMapValueType) 734 | m.Range(func(key GenericMapKeyType, value GenericMapValueType) bool { 735 | result[key] = value 736 | return true 737 | }) 738 | 739 | return result 740 | } 741 | 742 | //template:PublicMapTemplate 743 | 744 | //////////////////// 745 | /// Constructors /// 746 | //////////////////// 747 | 748 | // NewGenericMapType returns a new GenericMapType containing all items in items. 749 | func NewGenericMapType(items ...GenericMapItem) *GenericMapType { 750 | return newGenericMapType(items) 751 | } 752 | 753 | // NewGenericMapTypeFromNativeMap returns a new GenericMapType containing all items in m. 754 | func NewGenericMapTypeFromNativeMap(m map[GenericMapKeyType]GenericMapValueType) *GenericMapType { 755 | buckets := newPrivateGenericMapItemBuckets(len(m)) 756 | for key, value := range m { 757 | buckets.AddItem(GenericMapItem{Key: key, Value: value}) 758 | } 759 | 760 | return &GenericMapType{backingVector: emptyGenericMapItemBucketVector.Append(buckets.buckets...), len: buckets.length} 761 | } 762 | 763 | //template:SetTemplate 764 | 765 | // GenericSetType is a persistent set 766 | type GenericSetType struct { 767 | backingMap *GenericMapType 768 | } 769 | 770 | // NewGenericSetType returns a new GenericSetType containing items. 771 | func NewGenericSetType(items ...GenericMapKeyType) *GenericSetType { 772 | mapItems := make([]GenericMapItem, 0, len(items)) 773 | var mapValue GenericMapValueType 774 | for _, x := range items { 775 | mapItems = append(mapItems, GenericMapItem{Key: x, Value: mapValue}) 776 | } 777 | 778 | return &GenericSetType{backingMap: newGenericMapType(mapItems)} 779 | } 780 | 781 | // Add returns a new GenericSetType containing item. 782 | func (s *GenericSetType) Add(item GenericMapKeyType) *GenericSetType { 783 | var mapValue GenericMapValueType 784 | return &GenericSetType{backingMap: s.backingMap.Store(item, mapValue)} 785 | } 786 | 787 | // Delete returns a new GenericSetType without item. 788 | func (s *GenericSetType) Delete(item GenericMapKeyType) *GenericSetType { 789 | newMap := s.backingMap.Delete(item) 790 | if newMap == s.backingMap { 791 | return s 792 | } 793 | 794 | return &GenericSetType{backingMap: newMap} 795 | } 796 | 797 | // Contains returns true if item is present in s, false otherwise. 798 | func (s *GenericSetType) Contains(item GenericMapKeyType) bool { 799 | _, ok := s.backingMap.Load(item) 800 | return ok 801 | } 802 | 803 | // Range calls f repeatedly passing it each element in s as argument until either 804 | // all elements have been visited or f returns false. 805 | func (s *GenericSetType) Range(f func(GenericMapKeyType) bool) { 806 | s.backingMap.Range(func(k GenericMapKeyType, _ GenericMapValueType) bool { 807 | return f(k) 808 | }) 809 | } 810 | 811 | // IsSubset returns true if all elements in s are present in other, false otherwise. 812 | func (s *GenericSetType) IsSubset(other *GenericSetType) bool { 813 | if other.Len() < s.Len() { 814 | return false 815 | } 816 | 817 | isSubset := true 818 | s.Range(func(item GenericMapKeyType) bool { 819 | if !other.Contains(item) { 820 | isSubset = false 821 | } 822 | 823 | return isSubset 824 | }) 825 | 826 | return isSubset 827 | } 828 | 829 | // IsSuperset returns true if all elements in other are present in s, false otherwise. 830 | func (s *GenericSetType) IsSuperset(other *GenericSetType) bool { 831 | return other.IsSubset(s) 832 | } 833 | 834 | // Union returns a new GenericSetType containing all elements present 835 | // in either s or other. 836 | func (s *GenericSetType) Union(other *GenericSetType) *GenericSetType { 837 | result := s 838 | 839 | // Simplest possible solution right now. Would probable be more efficient 840 | // to concatenate two slices of elements from the two sets and create a 841 | // new set from that slice for many cases. 842 | other.Range(func(item GenericMapKeyType) bool { 843 | result = result.Add(item) 844 | return true 845 | }) 846 | 847 | return result 848 | } 849 | 850 | // Equals returns true if s and other contains the same elements, false otherwise. 851 | func (s *GenericSetType) Equals(other *GenericSetType) bool { 852 | return s.Len() == other.Len() && s.IsSubset(other) 853 | } 854 | 855 | func (s *GenericSetType) difference(other *GenericSetType) []GenericMapKeyType { 856 | items := make([]GenericMapKeyType, 0) 857 | s.Range(func(item GenericMapKeyType) bool { 858 | if !other.Contains(item) { 859 | items = append(items, item) 860 | } 861 | 862 | return true 863 | }) 864 | 865 | return items 866 | } 867 | 868 | // Difference returns a new GenericSetType containing all elements present 869 | // in s but not in other. 870 | func (s *GenericSetType) Difference(other *GenericSetType) *GenericSetType { 871 | return NewGenericSetType(s.difference(other)...) 872 | } 873 | 874 | // SymmetricDifference returns a new GenericSetType containing all elements present 875 | // in either s or other but not both. 876 | func (s *GenericSetType) SymmetricDifference(other *GenericSetType) *GenericSetType { 877 | items := s.difference(other) 878 | items = append(items, other.difference(s)...) 879 | return NewGenericSetType(items...) 880 | } 881 | 882 | // Intersection returns a new GenericSetType containing all elements present in both 883 | // s and other. 884 | func (s *GenericSetType) Intersection(other *GenericSetType) *GenericSetType { 885 | items := make([]GenericMapKeyType, 0) 886 | s.Range(func(item GenericMapKeyType) bool { 887 | if other.Contains(item) { 888 | items = append(items, item) 889 | } 890 | 891 | return true 892 | }) 893 | 894 | return NewGenericSetType(items...) 895 | } 896 | 897 | // Len returns the number of elements in s. 898 | func (s *GenericSetType) Len() int { 899 | return s.backingMap.Len() 900 | } 901 | 902 | // ToNativeSlice returns a native Go slice containing all elements of s. 903 | func (s *GenericSetType) ToNativeSlice() []GenericMapKeyType { 904 | items := make([]GenericMapKeyType, 0, s.Len()) 905 | s.Range(func(item GenericMapKeyType) bool { 906 | items = append(items, item) 907 | return true 908 | }) 909 | 910 | return items 911 | } 912 | 913 | //template:commentsNotWantedInGeneratedCode 914 | 915 | // peds -maps "FooMap;BarMap" 916 | // -sets "FooSet" 917 | // -vectors "FooVec" 918 | // -imports "io;github.com/my/mypackage" 919 | // -package mycontainers 920 | // -file mycontainers_gen.go 921 | -------------------------------------------------------------------------------- /internal/generic_types/functions.go: -------------------------------------------------------------------------------- 1 | package generic_types 2 | 3 | // This file contains function definitions needed for the template package to 4 | // be compilable but that should be replaced by other function calls in the templates 5 | 6 | func genericHash(x interface{}) uint32 { 7 | return interfaceHash(x) 8 | } 9 | -------------------------------------------------------------------------------- /internal/generic_types/types.go: -------------------------------------------------------------------------------- 1 | package generic_types 2 | 3 | // GenericType is a placeholder for types contained in vectors or slices. 4 | type GenericType int 5 | 6 | // GenericMapKeyType is a placeholder for types used as keys in maps or 7 | // values in slices. 8 | type GenericMapKeyType int 9 | 10 | // GenericMapValueType is a placeholder for types used as values in maps. 11 | type GenericMapValueType int 12 | -------------------------------------------------------------------------------- /internal/templates/templates.go: -------------------------------------------------------------------------------- 1 | package templates 2 | 3 | // NOTE: This file is auto generated, don't edit manually! 4 | const CommonTemplate string = ` 5 | import ( 6 | "encoding/binary" 7 | "fmt" 8 | "hash/crc32" 9 | "math" 10 | "unsafe" 11 | ) 12 | 13 | const shiftSize = 5 14 | const nodeSize = 32 15 | const shiftBitMask = 0x1F 16 | 17 | type commonNode interface{} 18 | 19 | var emptyCommonNode commonNode = []commonNode{} 20 | 21 | func uintMin(a, b uint) uint { 22 | if a < b { 23 | return a 24 | } 25 | 26 | return b 27 | } 28 | 29 | func newPath(shift uint, node commonNode) commonNode { 30 | if shift == 0 { 31 | return node 32 | } 33 | 34 | return newPath(shift-shiftSize, commonNode([]commonNode{node})) 35 | } 36 | 37 | func assertSliceOk(start, stop, len int) { 38 | if start < 0 { 39 | panic(fmt.Sprintf("Invalid slice index %d (index must be non-negative)", start)) 40 | } 41 | 42 | if start > stop { 43 | panic(fmt.Sprintf("Invalid slice index: %d > %d", start, stop)) 44 | } 45 | 46 | if stop > len { 47 | panic(fmt.Sprintf("Slice bounds out of range, start=%d, stop=%d, len=%d", start, stop, len)) 48 | } 49 | } 50 | 51 | const upperMapLoadFactor float64 = 8.0 52 | const lowerMapLoadFactor float64 = 2.0 53 | const initialMapLoadFactor float64 = (upperMapLoadFactor + lowerMapLoadFactor) / 2 54 | 55 | ////////////////////////// 56 | //// Hash functions ////// 57 | ////////////////////////// 58 | 59 | func hash(x []byte) uint32 { 60 | return crc32.ChecksumIEEE(x) 61 | } 62 | 63 | //go:noescape 64 | //go:linkname nilinterhash runtime.nilinterhash 65 | func nilinterhash(p unsafe.Pointer, h uintptr) uintptr 66 | 67 | func interfaceHash(x interface{}) uint32 { 68 | return uint32(nilinterhash(unsafe.Pointer(&x), 0)) 69 | } 70 | 71 | func byteHash(x byte) uint32 { 72 | return hash([]byte{x}) 73 | } 74 | 75 | func uint8Hash(x uint8) uint32 { 76 | return byteHash(byte(x)) 77 | } 78 | 79 | func int8Hash(x int8) uint32 { 80 | return uint8Hash(uint8(x)) 81 | } 82 | 83 | func uint16Hash(x uint16) uint32 { 84 | bX := make([]byte, 2) 85 | binary.LittleEndian.PutUint16(bX, x) 86 | return hash(bX) 87 | } 88 | 89 | func int16Hash(x int16) uint32 { 90 | return uint16Hash(uint16(x)) 91 | } 92 | 93 | func uint32Hash(x uint32) uint32 { 94 | bX := make([]byte, 4) 95 | binary.LittleEndian.PutUint32(bX, x) 96 | return hash(bX) 97 | } 98 | 99 | func int32Hash(x int32) uint32 { 100 | return uint32Hash(uint32(x)) 101 | } 102 | 103 | func uint64Hash(x uint64) uint32 { 104 | bX := make([]byte, 8) 105 | binary.LittleEndian.PutUint64(bX, x) 106 | return hash(bX) 107 | } 108 | 109 | func int64Hash(x int64) uint32 { 110 | return uint64Hash(uint64(x)) 111 | } 112 | 113 | func intHash(x int) uint32 { 114 | return int64Hash(int64(x)) 115 | } 116 | 117 | func uintHash(x uint) uint32 { 118 | return uint64Hash(uint64(x)) 119 | } 120 | 121 | func boolHash(x bool) uint32 { 122 | if x { 123 | return 1 124 | } 125 | 126 | return 0 127 | } 128 | 129 | func runeHash(x rune) uint32 { 130 | return int32Hash(int32(x)) 131 | } 132 | 133 | func stringHash(x string) uint32 { 134 | return hash([]byte(x)) 135 | } 136 | 137 | func float64Hash(x float64) uint32 { 138 | return uint64Hash(math.Float64bits(x)) 139 | } 140 | 141 | func float32Hash(x float32) uint32 { 142 | return uint32Hash(math.Float32bits(x)) 143 | } 144 | 145 | ` 146 | const PrivateMapTemplate string = ` 147 | /////////// 148 | /// Map /// 149 | /////////// 150 | 151 | ////////////////////// 152 | /// Backing vector /// 153 | ////////////////////// 154 | 155 | type private{{.MapItemTypeName}}BucketVector struct { 156 | tail []private{{.MapItemTypeName}}Bucket 157 | root commonNode 158 | len uint 159 | shift uint 160 | } 161 | 162 | type {{.MapItemTypeName}} struct { 163 | Key {{.MapKeyTypeName}} 164 | Value {{.MapValueTypeName}} 165 | } 166 | 167 | type private{{.MapItemTypeName}}Bucket []{{.MapItemTypeName}} 168 | 169 | var empty{{.MapItemTypeName}}BucketVectorTail = make([]private{{.MapItemTypeName}}Bucket, 0) 170 | var empty{{.MapItemTypeName}}BucketVector *private{{.MapItemTypeName}}BucketVector = &private{{.MapItemTypeName}}BucketVector{root: emptyCommonNode, shift: shiftSize, tail: empty{{.MapItemTypeName}}BucketVectorTail} 171 | 172 | func (v *private{{.MapItemTypeName}}BucketVector) Get(i int) private{{.MapItemTypeName}}Bucket { 173 | if i < 0 || uint(i) >= v.len { 174 | panic("Index out of bounds") 175 | } 176 | 177 | return v.sliceFor(uint(i))[i&shiftBitMask] 178 | } 179 | 180 | func (v *private{{.MapItemTypeName}}BucketVector) sliceFor(i uint) []private{{.MapItemTypeName}}Bucket { 181 | if i >= v.tailOffset() { 182 | return v.tail 183 | } 184 | 185 | node := v.root 186 | for level := v.shift; level > 0; level -= shiftSize { 187 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 188 | } 189 | 190 | return node.([]private{{.MapItemTypeName}}Bucket) 191 | } 192 | 193 | func (v *private{{.MapItemTypeName}}BucketVector) tailOffset() uint { 194 | if v.len < nodeSize { 195 | return 0 196 | } 197 | 198 | return ((v.len - 1) >> shiftSize) << shiftSize 199 | } 200 | 201 | func (v *private{{.MapItemTypeName}}BucketVector) Set(i int, item private{{.MapItemTypeName}}Bucket) *private{{.MapItemTypeName}}BucketVector { 202 | if i < 0 || uint(i) >= v.len { 203 | panic("Index out of bounds") 204 | } 205 | 206 | if uint(i) >= v.tailOffset() { 207 | newTail := make([]private{{.MapItemTypeName}}Bucket, len(v.tail)) 208 | copy(newTail, v.tail) 209 | newTail[i&shiftBitMask] = item 210 | return &private{{.MapItemTypeName}}BucketVector{root: v.root, tail: newTail, len: v.len, shift: v.shift} 211 | } 212 | 213 | return &private{{.MapItemTypeName}}BucketVector{root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 214 | } 215 | 216 | func (v *private{{.MapItemTypeName}}BucketVector) doAssoc(level uint, node commonNode, i uint, item private{{.MapItemTypeName}}Bucket) commonNode { 217 | if level == 0 { 218 | ret := make([]private{{.MapItemTypeName}}Bucket, nodeSize) 219 | copy(ret, node.([]private{{.MapItemTypeName}}Bucket)) 220 | ret[i&shiftBitMask] = item 221 | return ret 222 | } 223 | 224 | ret := make([]commonNode, nodeSize) 225 | copy(ret, node.([]commonNode)) 226 | subidx := (i >> level) & shiftBitMask 227 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 228 | return ret 229 | } 230 | 231 | func (v *private{{.MapItemTypeName}}BucketVector) pushTail(level uint, parent commonNode, tailNode []private{{.MapItemTypeName}}Bucket) commonNode { 232 | subIdx := ((v.len - 1) >> level) & shiftBitMask 233 | parentNode := parent.([]commonNode) 234 | ret := make([]commonNode, subIdx+1) 235 | copy(ret, parentNode) 236 | var nodeToInsert commonNode 237 | 238 | if level == shiftSize { 239 | nodeToInsert = tailNode 240 | } else if subIdx < uint(len(parentNode)) { 241 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 242 | } else { 243 | nodeToInsert = newPath(level-shiftSize, tailNode) 244 | } 245 | 246 | ret[subIdx] = nodeToInsert 247 | return ret 248 | } 249 | 250 | func (v *private{{.MapItemTypeName}}BucketVector) Append(item ...private{{.MapItemTypeName}}Bucket) *private{{.MapItemTypeName}}BucketVector { 251 | result := v 252 | itemLen := uint(len(item)) 253 | for insertOffset := uint(0); insertOffset < itemLen; { 254 | tailLen := result.len - result.tailOffset() 255 | tailFree := nodeSize - tailLen 256 | if tailFree == 0 { 257 | result = result.pushLeafNode(result.tail) 258 | result.tail = empty{{.MapItemTypeName}}BucketVector.tail 259 | tailFree = nodeSize 260 | tailLen = 0 261 | } 262 | 263 | batchLen := uintMin(itemLen-insertOffset, tailFree) 264 | newTail := make([]private{{.MapItemTypeName}}Bucket, 0, tailLen+batchLen) 265 | newTail = append(newTail, result.tail...) 266 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 267 | result = &private{{.MapItemTypeName}}BucketVector{root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 268 | insertOffset += batchLen 269 | } 270 | 271 | return result 272 | } 273 | 274 | func (v *private{{.MapItemTypeName}}BucketVector) pushLeafNode(node []private{{.MapItemTypeName}}Bucket) *private{{.MapItemTypeName}}BucketVector { 275 | var newRoot commonNode 276 | newShift := v.shift 277 | 278 | // Root overflow? 279 | if (v.len >> shiftSize) > (1 << v.shift) { 280 | newNode := newPath(v.shift, node) 281 | newRoot = commonNode([]commonNode{v.root, newNode}) 282 | newShift = v.shift + shiftSize 283 | } else { 284 | newRoot = v.pushTail(v.shift, v.root, node) 285 | } 286 | 287 | return &private{{.MapItemTypeName}}BucketVector{root: newRoot, tail: v.tail, len: v.len, shift: newShift} 288 | } 289 | 290 | func (v *private{{.MapItemTypeName}}BucketVector) Len() int { 291 | return int(v.len) 292 | } 293 | 294 | func (v *private{{.MapItemTypeName}}BucketVector) Range(f func(private{{.MapItemTypeName}}Bucket) bool) { 295 | var currentNode []private{{.MapItemTypeName}}Bucket 296 | for i := uint(0); i < v.len; i++ { 297 | if i&shiftBitMask == 0 { 298 | currentNode = v.sliceFor(uint(i)) 299 | } 300 | 301 | if !f(currentNode[i&shiftBitMask]) { 302 | return 303 | } 304 | } 305 | } 306 | 307 | // {{.MapTypeName}} is a persistent key - value map 308 | type {{.MapTypeName}} struct { 309 | backingVector *private{{.MapItemTypeName}}BucketVector 310 | len int 311 | } 312 | 313 | func (m *{{.MapTypeName}}) pos(key {{.MapKeyTypeName}}) int { 314 | return int(uint64({{.MapKeyHashFunc}}(key)) % uint64(m.backingVector.Len())) 315 | } 316 | 317 | // Helper type used during map creation and reallocation 318 | type private{{.MapItemTypeName}}Buckets struct { 319 | buckets []private{{.MapItemTypeName}}Bucket 320 | length int 321 | } 322 | 323 | func newPrivate{{.MapItemTypeName}}Buckets(itemCount int) *private{{.MapItemTypeName}}Buckets { 324 | size := int(float64(itemCount)/initialMapLoadFactor) + 1 325 | buckets := make([]private{{.MapItemTypeName}}Bucket, size) 326 | return &private{{.MapItemTypeName}}Buckets{buckets: buckets} 327 | } 328 | 329 | func (b *private{{.MapItemTypeName}}Buckets) AddItem(item {{.MapItemTypeName}}) { 330 | ix := int(uint64({{.MapKeyHashFunc}}(item.Key)) % uint64(len(b.buckets))) 331 | bucket := b.buckets[ix] 332 | if bucket != nil { 333 | // Hash collision, merge with existing bucket 334 | for keyIx, bItem := range bucket { 335 | if item.Key == bItem.Key { 336 | bucket[keyIx] = item 337 | return 338 | } 339 | } 340 | 341 | b.buckets[ix] = append(bucket, {{.MapItemTypeName}}{Key: item.Key, Value: item.Value}) 342 | b.length++ 343 | } else { 344 | bucket := make(private{{.MapItemTypeName}}Bucket, 0, int(math.Max(initialMapLoadFactor, 1.0))) 345 | b.buckets[ix] = append(bucket, item) 346 | b.length++ 347 | } 348 | } 349 | 350 | func (b *private{{.MapItemTypeName}}Buckets) AddItemsFromMap(m *{{.MapTypeName}}) { 351 | m.backingVector.Range(func(bucket private{{.MapItemTypeName}}Bucket) bool { 352 | for _, item := range bucket { 353 | b.AddItem(item) 354 | } 355 | return true 356 | }) 357 | } 358 | 359 | func new{{.MapTypeName}}(items []{{.MapItemTypeName}}) *{{.MapTypeName}} { 360 | buckets := newPrivate{{.MapItemTypeName}}Buckets(len(items)) 361 | for _, item := range items { 362 | buckets.AddItem(item) 363 | } 364 | 365 | return &{{.MapTypeName}}{backingVector: empty{{.MapItemTypeName}}BucketVector.Append(buckets.buckets...), len: buckets.length} 366 | } 367 | 368 | // Len returns the number of items in m. 369 | func (m *{{.MapTypeName}}) Len() int { 370 | return int(m.len) 371 | } 372 | 373 | // Load returns value identified by key. ok is set to true if key exists in the map, false otherwise. 374 | func (m *{{.MapTypeName}}) Load(key {{.MapKeyTypeName}}) (value {{.MapValueTypeName}}, ok bool) { 375 | bucket := m.backingVector.Get(m.pos(key)) 376 | if bucket != nil { 377 | for _, item := range bucket { 378 | if item.Key == key { 379 | return item.Value, true 380 | } 381 | } 382 | } 383 | 384 | var zeroValue {{.MapValueTypeName}} 385 | return zeroValue, false 386 | } 387 | 388 | // Store returns a new {{.MapTypeName}} containing value identified by key. 389 | func (m *{{.MapTypeName}}) Store(key {{.MapKeyTypeName}}, value {{.MapValueTypeName}}) *{{.MapTypeName}} { 390 | // Grow backing vector if load factor is too high 391 | if m.Len() >= m.backingVector.Len()*int(upperMapLoadFactor) { 392 | buckets := newPrivate{{.MapItemTypeName}}Buckets(m.Len() + 1) 393 | buckets.AddItemsFromMap(m) 394 | buckets.AddItem({{.MapItemTypeName}}{Key: key, Value: value}) 395 | return &{{.MapTypeName}}{backingVector: empty{{.MapItemTypeName}}BucketVector.Append(buckets.buckets...), len: buckets.length} 396 | } 397 | 398 | pos := m.pos(key) 399 | bucket := m.backingVector.Get(pos) 400 | if bucket != nil { 401 | for ix, item := range bucket { 402 | if item.Key == key { 403 | // Overwrite existing item 404 | newBucket := make(private{{.MapItemTypeName}}Bucket, len(bucket)) 405 | copy(newBucket, bucket) 406 | newBucket[ix] = {{.MapItemTypeName}}{Key: key, Value: value} 407 | return &{{.MapTypeName}}{backingVector: m.backingVector.Set(pos, newBucket), len: m.len} 408 | } 409 | } 410 | 411 | // Add new item to bucket 412 | newBucket := make(private{{.MapItemTypeName}}Bucket, len(bucket), len(bucket)+1) 413 | copy(newBucket, bucket) 414 | newBucket = append(newBucket, {{.MapItemTypeName}}{Key: key, Value: value}) 415 | return &{{.MapTypeName}}{backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 416 | } 417 | 418 | item := {{.MapItemTypeName}}{Key: key, Value: value} 419 | newBucket := private{{.MapItemTypeName}}Bucket{item} 420 | return &{{.MapTypeName}}{backingVector: m.backingVector.Set(pos, newBucket), len: m.len + 1} 421 | } 422 | 423 | // Delete returns a new {{.MapTypeName}} without the element identified by key. 424 | func (m *{{.MapTypeName}}) Delete(key {{.MapKeyTypeName}}) *{{.MapTypeName}} { 425 | pos := m.pos(key) 426 | bucket := m.backingVector.Get(pos) 427 | if bucket != nil { 428 | newBucket := make(private{{.MapItemTypeName}}Bucket, 0) 429 | for _, item := range bucket { 430 | if item.Key != key { 431 | newBucket = append(newBucket, item) 432 | } 433 | } 434 | 435 | removedItemCount := len(bucket) - len(newBucket) 436 | if removedItemCount == 0 { 437 | return m 438 | } 439 | 440 | if len(newBucket) == 0 { 441 | newBucket = nil 442 | } 443 | 444 | newMap := &{{.MapTypeName}}{backingVector: m.backingVector.Set(pos, newBucket), len: m.len - removedItemCount} 445 | if newMap.backingVector.Len() > 1 && newMap.Len() < newMap.backingVector.Len()*int(lowerMapLoadFactor) { 446 | // Shrink backing vector if needed to avoid occupying excessive space 447 | buckets := newPrivate{{.MapItemTypeName}}Buckets(newMap.Len()) 448 | buckets.AddItemsFromMap(newMap) 449 | return &{{.MapTypeName}}{backingVector: empty{{.MapItemTypeName}}BucketVector.Append(buckets.buckets...), len: buckets.length} 450 | } 451 | 452 | return newMap 453 | } 454 | 455 | return m 456 | } 457 | 458 | // Range calls f repeatedly passing it each key and value as argument until either 459 | // all elements have been visited or f returns false. 460 | func (m *{{.MapTypeName}}) Range(f func({{.MapKeyTypeName}}, {{.MapValueTypeName}}) bool) { 461 | m.backingVector.Range(func(bucket private{{.MapItemTypeName}}Bucket) bool { 462 | for _, item := range bucket { 463 | if !f(item.Key, item.Value) { 464 | return false 465 | } 466 | } 467 | return true 468 | }) 469 | } 470 | 471 | // ToNativeMap returns a native Go map containing all elements of m. 472 | func (m *{{.MapTypeName}}) ToNativeMap() map[{{.MapKeyTypeName}}]{{.MapValueTypeName}} { 473 | result := make(map[{{.MapKeyTypeName}}]{{.MapValueTypeName}}) 474 | m.Range(func(key {{.MapKeyTypeName}}, value {{.MapValueTypeName}}) bool { 475 | result[key] = value 476 | return true 477 | }) 478 | 479 | return result 480 | } 481 | 482 | ` 483 | const PublicMapTemplate string = ` 484 | //////////////////// 485 | /// Constructors /// 486 | //////////////////// 487 | 488 | // New{{.MapTypeName}} returns a new {{.MapTypeName}} containing all items in items. 489 | func New{{.MapTypeName}}(items ...{{.MapItemTypeName}}) *{{.MapTypeName}} { 490 | return new{{.MapTypeName}}(items) 491 | } 492 | 493 | // New{{.MapTypeName}}FromNativeMap returns a new {{.MapTypeName}} containing all items in m. 494 | func New{{.MapTypeName}}FromNativeMap(m map[{{.MapKeyTypeName}}]{{.MapValueTypeName}}) *{{.MapTypeName}} { 495 | buckets := newPrivate{{.MapItemTypeName}}Buckets(len(m)) 496 | for key, value := range m { 497 | buckets.AddItem({{.MapItemTypeName}}{Key: key, Value: value}) 498 | } 499 | 500 | return &{{.MapTypeName}}{backingVector: empty{{.MapItemTypeName}}BucketVector.Append(buckets.buckets...), len: buckets.length} 501 | } 502 | 503 | ` 504 | const SetTemplate string = ` 505 | // {{.SetTypeName}} is a persistent set 506 | type {{.SetTypeName}} struct { 507 | backingMap *{{.MapTypeName}} 508 | } 509 | 510 | // New{{.SetTypeName}} returns a new {{.SetTypeName}} containing items. 511 | func New{{.SetTypeName}}(items ...{{.MapKeyTypeName}}) *{{.SetTypeName}} { 512 | mapItems := make([]{{.MapItemTypeName}}, 0, len(items)) 513 | var mapValue {{.MapValueTypeName}} 514 | for _, x := range items { 515 | mapItems = append(mapItems, {{.MapItemTypeName}}{Key: x, Value: mapValue}) 516 | } 517 | 518 | return &{{.SetTypeName}}{backingMap: new{{.MapTypeName}}(mapItems)} 519 | } 520 | 521 | // Add returns a new {{.SetTypeName}} containing item. 522 | func (s *{{.SetTypeName}}) Add(item {{.MapKeyTypeName}}) *{{.SetTypeName}} { 523 | var mapValue {{.MapValueTypeName}} 524 | return &{{.SetTypeName}}{backingMap: s.backingMap.Store(item, mapValue)} 525 | } 526 | 527 | // Delete returns a new {{.SetTypeName}} without item. 528 | func (s *{{.SetTypeName}}) Delete(item {{.MapKeyTypeName}}) *{{.SetTypeName}} { 529 | newMap := s.backingMap.Delete(item) 530 | if newMap == s.backingMap { 531 | return s 532 | } 533 | 534 | return &{{.SetTypeName}}{backingMap: newMap} 535 | } 536 | 537 | // Contains returns true if item is present in s, false otherwise. 538 | func (s *{{.SetTypeName}}) Contains(item {{.MapKeyTypeName}}) bool { 539 | _, ok := s.backingMap.Load(item) 540 | return ok 541 | } 542 | 543 | // Range calls f repeatedly passing it each element in s as argument until either 544 | // all elements have been visited or f returns false. 545 | func (s *{{.SetTypeName}}) Range(f func({{.MapKeyTypeName}}) bool) { 546 | s.backingMap.Range(func(k {{.MapKeyTypeName}}, _ {{.MapValueTypeName}}) bool { 547 | return f(k) 548 | }) 549 | } 550 | 551 | // IsSubset returns true if all elements in s are present in other, false otherwise. 552 | func (s *{{.SetTypeName}}) IsSubset(other *{{.SetTypeName}}) bool { 553 | if other.Len() < s.Len() { 554 | return false 555 | } 556 | 557 | isSubset := true 558 | s.Range(func(item {{.MapKeyTypeName}}) bool { 559 | if !other.Contains(item) { 560 | isSubset = false 561 | } 562 | 563 | return isSubset 564 | }) 565 | 566 | return isSubset 567 | } 568 | 569 | // IsSuperset returns true if all elements in other are present in s, false otherwise. 570 | func (s *{{.SetTypeName}}) IsSuperset(other *{{.SetTypeName}}) bool { 571 | return other.IsSubset(s) 572 | } 573 | 574 | // Union returns a new {{.SetTypeName}} containing all elements present 575 | // in either s or other. 576 | func (s *{{.SetTypeName}}) Union(other *{{.SetTypeName}}) *{{.SetTypeName}} { 577 | result := s 578 | 579 | // Simplest possible solution right now. Would probable be more efficient 580 | // to concatenate two slices of elements from the two sets and create a 581 | // new set from that slice for many cases. 582 | other.Range(func(item {{.MapKeyTypeName}}) bool { 583 | result = result.Add(item) 584 | return true 585 | }) 586 | 587 | return result 588 | } 589 | 590 | // Equals returns true if s and other contains the same elements, false otherwise. 591 | func (s *{{.SetTypeName}}) Equals(other *{{.SetTypeName}}) bool { 592 | return s.Len() == other.Len() && s.IsSubset(other) 593 | } 594 | 595 | func (s *{{.SetTypeName}}) difference(other *{{.SetTypeName}}) []{{.MapKeyTypeName}} { 596 | items := make([]{{.MapKeyTypeName}}, 0) 597 | s.Range(func(item {{.MapKeyTypeName}}) bool { 598 | if !other.Contains(item) { 599 | items = append(items, item) 600 | } 601 | 602 | return true 603 | }) 604 | 605 | return items 606 | } 607 | 608 | // Difference returns a new {{.SetTypeName}} containing all elements present 609 | // in s but not in other. 610 | func (s *{{.SetTypeName}}) Difference(other *{{.SetTypeName}}) *{{.SetTypeName}} { 611 | return New{{.SetTypeName}}(s.difference(other)...) 612 | } 613 | 614 | // SymmetricDifference returns a new {{.SetTypeName}} containing all elements present 615 | // in either s or other but not both. 616 | func (s *{{.SetTypeName}}) SymmetricDifference(other *{{.SetTypeName}}) *{{.SetTypeName}} { 617 | items := s.difference(other) 618 | items = append(items, other.difference(s)...) 619 | return New{{.SetTypeName}}(items...) 620 | } 621 | 622 | // Intersection returns a new {{.SetTypeName}} containing all elements present in both 623 | // s and other. 624 | func (s *{{.SetTypeName}}) Intersection(other *{{.SetTypeName}}) *{{.SetTypeName}} { 625 | items := make([]{{.MapKeyTypeName}}, 0) 626 | s.Range(func(item {{.MapKeyTypeName}}) bool { 627 | if other.Contains(item) { 628 | items = append(items, item) 629 | } 630 | 631 | return true 632 | }) 633 | 634 | return New{{.SetTypeName}}(items...) 635 | } 636 | 637 | // Len returns the number of elements in s. 638 | func (s *{{.SetTypeName}}) Len() int { 639 | return s.backingMap.Len() 640 | } 641 | 642 | // ToNativeSlice returns a native Go slice containing all elements of s. 643 | func (s *{{.SetTypeName}}) ToNativeSlice() []{{.MapKeyTypeName}} { 644 | items := make([]{{.MapKeyTypeName}}, 0, s.Len()) 645 | s.Range(func(item {{.MapKeyTypeName}}) bool { 646 | items = append(items, item) 647 | return true 648 | }) 649 | 650 | return items 651 | } 652 | 653 | ` 654 | const SliceTemplate string = ` 655 | //////////////// 656 | //// Slice ///// 657 | //////////////// 658 | 659 | // {{.VectorTypeName}}Slice is a slice type backed by a {{.VectorTypeName}}. 660 | type {{.VectorTypeName}}Slice struct { 661 | vector *{{.VectorTypeName}} 662 | start, stop int 663 | } 664 | 665 | // New{{.VectorTypeName}}Slice returns a new New{{.VectorTypeName}}Slice containing the items provided in items. 666 | func New{{.VectorTypeName}}Slice(items ...{{.TypeName}}) *{{.VectorTypeName}}Slice { 667 | return &{{.VectorTypeName}}Slice{vector: empty{{.VectorTypeName}}.Append(items...), start: 0, stop: len(items)} 668 | } 669 | 670 | // Len returns the length of s. 671 | func (s *{{.VectorTypeName}}Slice) Len() int { 672 | return s.stop - s.start 673 | } 674 | 675 | // Get returns the element at position i. 676 | func (s *{{.VectorTypeName}}Slice) Get(i int) {{.TypeName}} { 677 | if i < 0 || s.start+i >= s.stop { 678 | panic("Index out of bounds") 679 | } 680 | 681 | return s.vector.Get(s.start + i) 682 | } 683 | 684 | // Set returns a new slice with the element at position i set to item. 685 | func (s *{{.VectorTypeName}}Slice) Set(i int, item {{.TypeName}}) *{{.VectorTypeName}}Slice { 686 | if i < 0 || s.start+i >= s.stop { 687 | panic("Index out of bounds") 688 | } 689 | 690 | return s.vector.Set(s.start+i, item).Slice(s.start, s.stop) 691 | } 692 | 693 | // Append returns a new slice with item(s) appended to it. 694 | func (s *{{.VectorTypeName}}Slice) Append(items ...{{.TypeName}}) *{{.VectorTypeName}}Slice { 695 | newSlice := {{.VectorTypeName}}Slice{vector: s.vector, start: s.start, stop: s.stop + len(items)} 696 | 697 | // If this is v slice that has an upper bound that is lower than the backing 698 | // vector then set the values in the backing vector to achieve some structural 699 | // sharing. 700 | itemPos := 0 701 | for ; s.stop+itemPos < s.vector.Len() && itemPos < len(items); itemPos++ { 702 | newSlice.vector = newSlice.vector.Set(s.stop+itemPos, items[itemPos]) 703 | } 704 | 705 | // For the rest just append it to the underlying vector 706 | newSlice.vector = newSlice.vector.Append(items[itemPos:]...) 707 | return &newSlice 708 | } 709 | 710 | // Slice returns a {{.VectorTypeName}}Slice that refers to all elements [start,stop) in s. 711 | func (s *{{.VectorTypeName}}Slice) Slice(start, stop int) *{{.VectorTypeName}}Slice { 712 | assertSliceOk(start, stop, s.stop-s.start) 713 | return &{{.VectorTypeName}}Slice{vector: s.vector, start: s.start + start, stop: s.start + stop} 714 | } 715 | 716 | // Range calls f repeatedly passing it each element in s in order as argument until either 717 | // all elements have been visited or f returns false. 718 | func (s *{{.VectorTypeName}}Slice) Range(f func({{.TypeName}}) bool) { 719 | var currentNode []{{.TypeName}} 720 | for i := uint(s.start); i < uint(s.stop); i++ { 721 | if i&shiftBitMask == 0 || i == uint(s.start) { 722 | currentNode = s.vector.sliceFor(uint(i)) 723 | } 724 | 725 | if !f(currentNode[i&shiftBitMask]) { 726 | return 727 | } 728 | } 729 | } 730 | 731 | ` 732 | const VectorTemplate string = ` 733 | ////////////// 734 | /// Vector /// 735 | ////////////// 736 | 737 | // A {{.VectorTypeName}} is an ordered persistent/immutable collection of items corresponding roughly 738 | // to the use cases for a slice. 739 | type {{.VectorTypeName}} struct { 740 | tail []{{.TypeName}} 741 | root commonNode 742 | len uint 743 | shift uint 744 | } 745 | 746 | var empty{{.VectorTypeName}}Tail = make([]{{.TypeName}}, 0) 747 | var empty{{.VectorTypeName}} *{{.VectorTypeName}} = &{{.VectorTypeName}}{root: emptyCommonNode, shift: shiftSize, tail: empty{{.VectorTypeName}}Tail} 748 | 749 | // New{{.VectorTypeName}} returns a new {{.VectorTypeName}} containing the items provided in items. 750 | func New{{.VectorTypeName}}(items ...{{.TypeName}}) *{{.VectorTypeName}} { 751 | return empty{{.VectorTypeName}}.Append(items...) 752 | } 753 | 754 | // Get returns the element at position i. 755 | func (v *{{.VectorTypeName}}) Get(i int) {{.TypeName}} { 756 | if i < 0 || uint(i) >= v.len { 757 | panic("Index out of bounds") 758 | } 759 | 760 | return v.sliceFor(uint(i))[i&shiftBitMask] 761 | } 762 | 763 | func (v *{{.VectorTypeName}}) sliceFor(i uint) []{{.TypeName}} { 764 | if i >= v.tailOffset() { 765 | return v.tail 766 | } 767 | 768 | node := v.root 769 | for level := v.shift; level > 0; level -= shiftSize { 770 | node = node.([]commonNode)[(i>>level)&shiftBitMask] 771 | } 772 | 773 | return node.([]{{.TypeName}}) 774 | } 775 | 776 | func (v *{{.VectorTypeName}}) tailOffset() uint { 777 | if v.len < nodeSize { 778 | return 0 779 | } 780 | 781 | return ((v.len - 1) >> shiftSize) << shiftSize 782 | } 783 | 784 | // Set returns a new vector with the element at position i set to item. 785 | func (v *{{.VectorTypeName}}) Set(i int, item {{.TypeName}}) *{{.VectorTypeName}} { 786 | if i < 0 || uint(i) >= v.len { 787 | panic("Index out of bounds") 788 | } 789 | 790 | if uint(i) >= v.tailOffset() { 791 | newTail := make([]{{.TypeName}}, len(v.tail)) 792 | copy(newTail, v.tail) 793 | newTail[i&shiftBitMask] = item 794 | return &{{.VectorTypeName}}{root: v.root, tail: newTail, len: v.len, shift: v.shift} 795 | } 796 | 797 | return &{{.VectorTypeName}}{root: v.doAssoc(v.shift, v.root, uint(i), item), tail: v.tail, len: v.len, shift: v.shift} 798 | } 799 | 800 | func (v *{{.VectorTypeName}}) doAssoc(level uint, node commonNode, i uint, item {{.TypeName}}) commonNode { 801 | if level == 0 { 802 | ret := make([]{{.TypeName}}, nodeSize) 803 | copy(ret, node.([]{{.TypeName}})) 804 | ret[i&shiftBitMask] = item 805 | return ret 806 | } 807 | 808 | ret := make([]commonNode, nodeSize) 809 | copy(ret, node.([]commonNode)) 810 | subidx := (i >> level) & shiftBitMask 811 | ret[subidx] = v.doAssoc(level-shiftSize, ret[subidx], i, item) 812 | return ret 813 | } 814 | 815 | func (v *{{.VectorTypeName}}) pushTail(level uint, parent commonNode, tailNode []{{.TypeName}}) commonNode { 816 | subIdx := ((v.len - 1) >> level) & shiftBitMask 817 | parentNode := parent.([]commonNode) 818 | ret := make([]commonNode, subIdx+1) 819 | copy(ret, parentNode) 820 | var nodeToInsert commonNode 821 | 822 | if level == shiftSize { 823 | nodeToInsert = tailNode 824 | } else if subIdx < uint(len(parentNode)) { 825 | nodeToInsert = v.pushTail(level-shiftSize, parentNode[subIdx], tailNode) 826 | } else { 827 | nodeToInsert = newPath(level-shiftSize, tailNode) 828 | } 829 | 830 | ret[subIdx] = nodeToInsert 831 | return ret 832 | } 833 | 834 | // Append returns a new vector with item(s) appended to it. 835 | func (v *{{.VectorTypeName}}) Append(item ...{{.TypeName}}) *{{.VectorTypeName}} { 836 | result := v 837 | itemLen := uint(len(item)) 838 | for insertOffset := uint(0); insertOffset < itemLen; { 839 | tailLen := result.len - result.tailOffset() 840 | tailFree := nodeSize - tailLen 841 | if tailFree == 0 { 842 | result = result.pushLeafNode(result.tail) 843 | result.tail = empty{{.VectorTypeName}}.tail 844 | tailFree = nodeSize 845 | tailLen = 0 846 | } 847 | 848 | batchLen := uintMin(itemLen-insertOffset, tailFree) 849 | newTail := make([]{{.TypeName}}, 0, tailLen+batchLen) 850 | newTail = append(newTail, result.tail...) 851 | newTail = append(newTail, item[insertOffset:insertOffset+batchLen]...) 852 | result = &{{.VectorTypeName}}{root: result.root, tail: newTail, len: result.len + batchLen, shift: result.shift} 853 | insertOffset += batchLen 854 | } 855 | 856 | return result 857 | } 858 | 859 | func (v *{{.VectorTypeName}}) pushLeafNode(node []{{.TypeName}}) *{{.VectorTypeName}} { 860 | var newRoot commonNode 861 | newShift := v.shift 862 | 863 | // Root overflow? 864 | if (v.len >> shiftSize) > (1 << v.shift) { 865 | newNode := newPath(v.shift, node) 866 | newRoot = commonNode([]commonNode{v.root, newNode}) 867 | newShift = v.shift + shiftSize 868 | } else { 869 | newRoot = v.pushTail(v.shift, v.root, node) 870 | } 871 | 872 | return &{{.VectorTypeName}}{root: newRoot, tail: v.tail, len: v.len, shift: newShift} 873 | } 874 | 875 | // Slice returns a {{.VectorTypeName}}Slice that refers to all elements [start,stop) in v. 876 | func (v *{{.VectorTypeName}}) Slice(start, stop int) *{{.VectorTypeName}}Slice { 877 | assertSliceOk(start, stop, v.Len()) 878 | return &{{.VectorTypeName}}Slice{vector: v, start: start, stop: stop} 879 | } 880 | 881 | // Len returns the length of v. 882 | func (v *{{.VectorTypeName}}) Len() int { 883 | return int(v.len) 884 | } 885 | 886 | // Range calls f repeatedly passing it each element in v in order as argument until either 887 | // all elements have been visited or f returns false. 888 | func (v *{{.VectorTypeName}}) Range(f func({{.TypeName}}) bool) { 889 | var currentNode []{{.TypeName}} 890 | for i := uint(0); i < v.len; i++ { 891 | if i&shiftBitMask == 0 { 892 | currentNode = v.sliceFor(i) 893 | } 894 | 895 | if !f(currentNode[i&shiftBitMask]) { 896 | return 897 | } 898 | } 899 | } 900 | 901 | // ToNativeSlice returns a Go slice containing all elements of v 902 | func (v *{{.VectorTypeName}}) ToNativeSlice() []{{.TypeName}} { 903 | result := make([]{{.TypeName}}, 0, v.len) 904 | for i := uint(0); i < v.len; i += nodeSize { 905 | result = append(result, v.sliceFor(i)...) 906 | } 907 | 908 | return result 909 | } 910 | 911 | ` 912 | const commentsNotWantedInGeneratedCode string = ` 913 | // peds -maps "FooMap;BarMap" 914 | // -sets "FooSet" 915 | // -vectors "FooVec" 916 | // -imports "io;github.com/my/mypackage" 917 | // -package mycontainers 918 | // -file mycontainers_gen.go 919 | ` 920 | -------------------------------------------------------------------------------- /tests/map_test.go: -------------------------------------------------------------------------------- 1 | package peds_testing 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "hash/crc32" 7 | "testing" 8 | ) 9 | 10 | func TestLenOfNewMap(t *testing.T) { 11 | m := NewStringIntMap() 12 | assertEqual(t, 0, m.Len()) 13 | 14 | m2 := NewStringIntMap(StringIntMapItem{Key: "a", Value: 1}) 15 | assertEqual(t, 1, m2.Len()) 16 | 17 | m3 := NewStringIntMap(StringIntMapItem{Key: "a", Value: 1}, StringIntMapItem{Key: "b", Value: 2}) 18 | assertEqual(t, 2, m3.Len()) 19 | } 20 | 21 | func TestLoadAndStore(t *testing.T) { 22 | m := NewStringIntMap() 23 | 24 | m2 := m.Store("a", 1) 25 | assertEqual(t, 0, m.Len()) 26 | assertEqual(t, 1, m2.Len()) 27 | 28 | v, ok := m.Load("a") 29 | assertEqual(t, 0, v) 30 | assertEqualBool(t, false, ok) 31 | 32 | v, ok = m2.Load("a") 33 | assertEqual(t, 1, v) 34 | assertEqualBool(t, true, ok) 35 | } 36 | 37 | func TestLoadAndStoreIntKey(t *testing.T) { 38 | m := NewIntStringMap() 39 | 40 | m2 := m.Store(1, "") 41 | v, _ := m.Load(2) 42 | assertEqualString(t, "", v) 43 | 44 | v, _ = m2.Load(1) 45 | assertEqualString(t, "", v) 46 | } 47 | 48 | func TestLoadAndDeleteExistingItem(t *testing.T) { 49 | m := NewStringIntMap() 50 | m2 := m.Store("a", 1) 51 | m3 := m.Delete("a") 52 | 53 | assertEqual(t, 0, m3.Len()) 54 | assertEqual(t, 1, m2.Len()) 55 | 56 | v, ok := m2.Load("a") 57 | assertEqualBool(t, true, ok) 58 | assertEqual(t, 1, v) 59 | 60 | v, ok = m3.Load("a") 61 | assertEqualBool(t, false, ok) 62 | assertEqual(t, 0, v) 63 | } 64 | 65 | func TestLoadAndDeleteNonExistingItem(t *testing.T) { 66 | m := NewStringIntMap() 67 | m2 := m.Store("a", 1) 68 | m3 := m2.Delete("b") 69 | 70 | assertEqual(t, 1, m3.Len()) 71 | assertEqual(t, 1, m2.Len()) 72 | 73 | v, ok := m2.Load("a") 74 | assertEqualBool(t, true, ok) 75 | assertEqual(t, 1, v) 76 | 77 | if m2 != m3 { 78 | t.Errorf("m2 and m3 are not the same object: %p != %p", m2, m3) 79 | } 80 | } 81 | 82 | func TestRangeAllItems(t *testing.T) { 83 | m := NewStringIntMap(StringIntMapItem{Key: "a", Value: 1}, StringIntMapItem{Key: "b", Value: 2}, StringIntMapItem{Key: "c", Value: 3}) 84 | sum := 0 85 | m.Range(func(key string, value int) bool { 86 | sum += value 87 | return true 88 | }) 89 | assertEqual(t, 6, sum) 90 | } 91 | 92 | func TestRangeStopOnKey(t *testing.T) { 93 | m := NewStringIntMap(StringIntMapItem{Key: "a", Value: 1}, StringIntMapItem{Key: "b", Value: 2}, StringIntMapItem{Key: "c", Value: 3}) 94 | count := 0 95 | m.Range(func(key string, value int) bool { 96 | if key == "c" || key == "b" { 97 | return false 98 | } 99 | 100 | count++ 101 | return true 102 | }) 103 | 104 | if count > 1 { 105 | t.Errorf("Did not expect count to be more than 1") 106 | } 107 | } 108 | 109 | func TestLargeInsertLookupDelete(t *testing.T) { 110 | size := 50000 111 | m := NewStringIntMap() 112 | for j := 0; j < size; j++ { 113 | m = m.Store(fmt.Sprintf("%d", j), j) 114 | } 115 | 116 | for j := 0; j < size; j++ { 117 | v, ok := m.Load(fmt.Sprintf("%d", j)) 118 | assertEqualBool(t, true, ok) 119 | assertEqual(t, v, j) 120 | } 121 | 122 | for j := 0; j < size; j++ { 123 | key := fmt.Sprintf("%d", j) 124 | m = m.Delete(key) 125 | assertEqual(t, size-j-1, m.Len()) 126 | _, ok := m.Load(key) 127 | assertEqualBool(t, false, ok) 128 | } 129 | } 130 | 131 | func TestFromToNativeMap(t *testing.T) { 132 | input := map[string]int{ 133 | "a": 1, 134 | "b": 2, 135 | "c": 3} 136 | m := NewStringIntMapFromNativeMap(input) 137 | output := m.ToNativeMap() 138 | assertEqual(t, len(input), len(output)) 139 | for key, value := range input { 140 | assertEqual(t, value, output[key]) 141 | } 142 | } 143 | 144 | ////////////////// 145 | /// Benchmarks /// 146 | ////////////////// 147 | 148 | func BenchmarkInsertMap(b *testing.B) { 149 | length := 0 150 | for i := 0; i < b.N; i++ { 151 | m := NewIntStringMap() 152 | for j := 0; j < 1000; j++ { 153 | m = m.Store(j, "a") 154 | } 155 | 156 | length += m.Len() 157 | } 158 | 159 | fmt.Println("Total length", length) 160 | } 161 | 162 | func BenchmarkInsertNativeMap(b *testing.B) { 163 | length := 0 164 | for i := 0; i < b.N; i++ { 165 | m := map[int]string{} 166 | for j := 0; j < 1000; j++ { 167 | m[j] = "a" 168 | } 169 | 170 | length += len(m) 171 | } 172 | 173 | fmt.Println("Total length", length) 174 | } 175 | 176 | func BenchmarkAccessMap(b *testing.B) { 177 | m := NewIntStringMap() 178 | for j := 0; j < 1000; j++ { 179 | m = m.Store(j, "a") 180 | } 181 | 182 | b.ResetTimer() 183 | for i := 0; i < b.N; i++ { 184 | for j := 0; j < 1000; j++ { 185 | _, _ = m.Load(j) 186 | } 187 | } 188 | } 189 | 190 | func BenchmarkAccessNativeMap(b *testing.B) { 191 | m := map[int]string{} 192 | for j := 0; j < 1000; j++ { 193 | m[j] = "a" 194 | } 195 | 196 | b.ResetTimer() 197 | for i := 0; i < b.N; i++ { 198 | for j := 0; j < 1000; j++ { 199 | _, _ = m[j] 200 | } 201 | } 202 | } 203 | 204 | func BenchmarkInterfaceHash(b *testing.B) { 205 | b.ReportAllocs() 206 | result := uint32(0) 207 | for i := 0; i < b.N; i++ { 208 | result += interfaceHash(i) 209 | } 210 | 211 | fmt.Println(result) 212 | } 213 | 214 | func intHashFunc(x int) uint32 { 215 | // Adler32 is the quickest by far of the hash functions provided in the stdlib but its distribution is bad. 216 | // CRC32 has a fairly good distribution and is fairly quick. 217 | bX := make([]byte, 8) 218 | binary.LittleEndian.PutUint64(bX, uint64(x)) 219 | return crc32.ChecksumIEEE(bX) 220 | } 221 | 222 | func BenchmarkIntHash(b *testing.B) { 223 | b.ReportAllocs() 224 | result := uint32(0) 225 | for i := 0; i < b.N; i++ { 226 | result += intHashFunc(i) 227 | } 228 | 229 | fmt.Println(result) 230 | } 231 | 232 | func BenchmarkLargeInsertAndLookup(b *testing.B) { 233 | b.ReportAllocs() 234 | total := 0 235 | for i := 0; i < b.N; i++ { 236 | 237 | m := NewStringIntMap() 238 | total = 0 239 | for j := 0; j < 100000; j++ { 240 | m = m.Store(fmt.Sprintf("%d", j), j) 241 | } 242 | 243 | for j := 0; j < 100000; j++ { 244 | v, _ := m.Load(fmt.Sprintf("%d", j)) 245 | total += v 246 | } 247 | 248 | } 249 | fmt.Println(total) 250 | } 251 | 252 | func BenchmarkLargeCreateInsertAndLookup(b *testing.B) { 253 | b.ReportAllocs() 254 | total := 0 255 | for i := 0; i < b.N; i++ { 256 | input := make([]StringIntMapItem, 0, 100000) 257 | for j := 0; j < 100000; j++ { 258 | input = append(input, StringIntMapItem{Key: fmt.Sprintf("%d", j), Value: j}) 259 | } 260 | 261 | m := NewStringIntMap(input...) 262 | total = 0 263 | 264 | for j := 0; j < 100000; j++ { 265 | v, _ := m.Load(fmt.Sprintf("%d", j)) 266 | total += v 267 | } 268 | 269 | } 270 | fmt.Println(total) 271 | } 272 | 273 | /* 274 | Profiling commands: 275 | 276 | # Run specific benchmark 277 | go test -bench=BenchmarkInsertMap -benchmem -run=^$ -memprofile=insert.mprof -cpuprofile=insert.prof --memprofilerate 1 278 | 279 | # CPU 280 | go tool pprof tests.test insert.prof 281 | 282 | # Memory 283 | go tool pprof --alloc_objects tests.test insert.mprof 284 | 285 | */ 286 | 287 | /* TODO: - Improve parsing of specs to allow white spaces etc. 288 | - More tests, invalid specs 289 | - Custom imports? 290 | - Non comparable types cannot be used as keys (should be detected during compilation) 291 | - Test custom struct as key 292 | - Make it possible to explicitly state which hash function to use and/or which the actual underlying type is 293 | */ 294 | -------------------------------------------------------------------------------- /tests/set_test.go: -------------------------------------------------------------------------------- 1 | package peds_testing 2 | 3 | import "testing" 4 | 5 | func TestSetAdd(t *testing.T) { 6 | s := NewFooSet() 7 | assertEqual(t, 0, s.Len()) 8 | 9 | s2 := s.Add(1) 10 | assertEqualBool(t, false, s.Contains(1)) 11 | assertEqualBool(t, true, s2.Contains(1)) 12 | assertEqualBool(t, false, s2.Contains(2)) 13 | 14 | } 15 | 16 | func TestSetDelete(t *testing.T) { 17 | s := NewFooSet(1, 2, 3) 18 | assertEqual(t, 3, s.Len()) 19 | assertEqualBool(t, true, s.Contains(1)) 20 | 21 | s2 := s.Delete(1) 22 | assertEqual(t, 2, s2.Len()) 23 | assertEqualBool(t, false, s2.Contains(1)) 24 | 25 | assertEqualBool(t, true, s.Contains(1)) 26 | } 27 | 28 | func TestSetIsSubset(t *testing.T) { 29 | t.Run("Empty sets are subsets of empty sets", func(t *testing.T) { 30 | assertEqualBool(t, true, NewFooSet().IsSubset(NewFooSet())) 31 | }) 32 | 33 | t.Run("Empty sets are subsets of non empty sets", func(t *testing.T) { 34 | assertEqualBool(t, true, NewFooSet().IsSubset(NewFooSet(1, 2, 3))) 35 | }) 36 | 37 | t.Run("Equal non-empty sets are subsets of each other", func(t *testing.T) { 38 | assertEqualBool(t, true, NewFooSet(1, 2).IsSubset(NewFooSet(1, 2))) 39 | }) 40 | 41 | t.Run("Strict subset", func(t *testing.T) { 42 | assertEqualBool(t, true, NewFooSet(1, 2).IsSubset(NewFooSet(1, 2, 3))) 43 | }) 44 | 45 | t.Run("Overlapping but not subset", func(t *testing.T) { 46 | assertEqualBool(t, false, NewFooSet(1, 2).IsSubset(NewFooSet(2, 3))) 47 | }) 48 | 49 | t.Run("Non overlapping", func(t *testing.T) { 50 | assertEqualBool(t, false, NewFooSet(1, 2).IsSubset(NewFooSet(3, 4))) 51 | }) 52 | } 53 | 54 | func TestSetIsSuperset(t *testing.T) { 55 | t.Run("Empty sets are supersets of empty sets", func(t *testing.T) { 56 | assertEqualBool(t, true, NewFooSet().IsSuperset(NewFooSet())) 57 | }) 58 | 59 | t.Run("Empty sets are not supsets of non empty sets", func(t *testing.T) { 60 | assertEqualBool(t, false, NewFooSet().IsSuperset(NewFooSet(1, 2, 3))) 61 | }) 62 | 63 | t.Run("Equal non-empty sets are supersets of each other", func(t *testing.T) { 64 | assertEqualBool(t, true, NewFooSet(1, 2).IsSuperset(NewFooSet(1, 2))) 65 | }) 66 | 67 | t.Run("Strict superset", func(t *testing.T) { 68 | assertEqualBool(t, true, NewFooSet(1, 2, 3).IsSuperset(NewFooSet(1, 2))) 69 | }) 70 | 71 | t.Run("Overlapping but not superset", func(t *testing.T) { 72 | assertEqualBool(t, false, NewFooSet(1, 2).IsSuperset(NewFooSet(2, 3))) 73 | }) 74 | 75 | t.Run("Non overlapping", func(t *testing.T) { 76 | assertEqualBool(t, false, NewFooSet(1, 2).IsSuperset(NewFooSet(3, 4))) 77 | }) 78 | } 79 | 80 | func assertSetsEqual(s1, s2 *FooSet) bool { 81 | return s1.Equals(s2) 82 | } 83 | 84 | func TestSetUnion(t *testing.T) { 85 | emptySet := NewFooSet() 86 | 87 | t.Run("Empty sets union empty set is an empty set", func(t *testing.T) { 88 | assertEqualBool(t, true, assertSetsEqual(emptySet, emptySet.Union(emptySet))) 89 | }) 90 | 91 | t.Run("Empty sets union non empty set is non empty set", func(t *testing.T) { 92 | assertEqualBool(t, true, assertSetsEqual(NewFooSet(1, 2, 3), emptySet.Union(NewFooSet(1, 2, 3)))) 93 | }) 94 | 95 | t.Run("Non empty sets union non empty contains all elements from both sets", func(t *testing.T) { 96 | assertEqualBool(t, true, assertSetsEqual(NewFooSet(1, 2, 3), NewFooSet(1, 2).Union(NewFooSet(2, 3)))) 97 | }) 98 | 99 | } 100 | 101 | func TestSetDifference(t *testing.T) { 102 | emptySet := NewFooSet() 103 | nonEmptySet := NewFooSet(1, 2, 3) 104 | 105 | t.Run("Difference between empty sets are empty sets", func(t *testing.T) { 106 | assertEqualBool(t, true, assertSetsEqual(emptySet, emptySet.Difference(emptySet))) 107 | }) 108 | 109 | t.Run("Difference between non empty set and empty set is the non empty set", func(t *testing.T) { 110 | assertEqualBool(t, true, assertSetsEqual(nonEmptySet, nonEmptySet.Difference(emptySet))) 111 | }) 112 | 113 | t.Run("Difference between empty set and non empty set is the empty set", func(t *testing.T) { 114 | assertEqualBool(t, true, assertSetsEqual(emptySet, emptySet.Difference(nonEmptySet))) 115 | }) 116 | 117 | t.Run("Difference results in all elements part of first set but not second", func(t *testing.T) { 118 | assertEqualBool(t, true, assertSetsEqual(NewFooSet(1), NewFooSet(1, 2).Difference(NewFooSet(2, 3)))) 119 | }) 120 | } 121 | 122 | func TestSetSymmetricDifference(t *testing.T) { 123 | emptySet := NewFooSet() 124 | nonEmptySet := NewFooSet(1, 2, 3) 125 | 126 | t.Run("Symmetric difference between empty sets are empty sets", func(t *testing.T) { 127 | assertEqualBool(t, true, assertSetsEqual(emptySet, emptySet.SymmetricDifference(emptySet))) 128 | }) 129 | 130 | t.Run("Symmetric difference between non empty set and empty set is the non empty set", func(t *testing.T) { 131 | assertEqualBool(t, true, assertSetsEqual(nonEmptySet, nonEmptySet.SymmetricDifference(emptySet))) 132 | }) 133 | 134 | t.Run("Symmetric difference between empty set and non empty set is the non empty set", func(t *testing.T) { 135 | assertEqualBool(t, true, assertSetsEqual(nonEmptySet, emptySet.SymmetricDifference(nonEmptySet))) 136 | }) 137 | 138 | t.Run("Symmetric difference is all elements part of first or second set but not both", func(t *testing.T) { 139 | assertEqualBool(t, true, assertSetsEqual(NewFooSet(1, 3), NewFooSet(1, 2).SymmetricDifference(NewFooSet(2, 3)))) 140 | }) 141 | } 142 | 143 | func TestSetIntersection(t *testing.T) { 144 | emptySet := NewFooSet() 145 | nonEmptySet := NewFooSet(1, 2, 3) 146 | 147 | t.Run("Intersection between empty sets are empty sets", func(t *testing.T) { 148 | assertEqualBool(t, true, assertSetsEqual(emptySet, emptySet.Intersection(emptySet))) 149 | }) 150 | 151 | t.Run("Intersection between non empty set and empty set is the empty set", func(t *testing.T) { 152 | assertEqualBool(t, true, assertSetsEqual(emptySet, nonEmptySet.Intersection(emptySet))) 153 | }) 154 | 155 | t.Run("Intersection results in all elements part of first and second set", func(t *testing.T) { 156 | assertEqualBool(t, true, assertSetsEqual(NewFooSet(2), NewFooSet(1, 2).Intersection(NewFooSet(2, 3)))) 157 | }) 158 | } 159 | 160 | func TestSetToNativeSlice(t *testing.T) { 161 | set := NewIntSet(1, 2, 3) 162 | theSlice := set.ToNativeSlice() 163 | assertEqual(t, 3, len(theSlice)) 164 | assertEqual(t, 1, theSlice[0]) 165 | assertEqual(t, 2, theSlice[1]) 166 | assertEqual(t, 3, theSlice[2]) 167 | } 168 | -------------------------------------------------------------------------------- /tests/subpackage/types.go: -------------------------------------------------------------------------------- 1 | package subpackage 2 | 3 | type Baz int 4 | -------------------------------------------------------------------------------- /tests/subpackage2/README.md: -------------------------------------------------------------------------------- 1 | Empty package where generated types will go during testing. -------------------------------------------------------------------------------- /tests/types.go: -------------------------------------------------------------------------------- 1 | package peds_testing 2 | 3 | // Types for testing. 4 | // Defined in a separate file from the tests since they are not picked up 5 | // if defined in a files with a *_test.go name. 6 | type Foo uint 7 | type Bar float64 8 | 9 | // NOTE: The awkward quoting below is just to test that white spaces in the type specifications are ignored. 10 | // If you stay away from using white space the quoting should not be required. 11 | //go:generate peds "-vectors=\"FooVector; IntVector;ImportVector\"" "-maps=\"StringIntMap;IntStringMap\"" "-sets=\"FooSet; IntSet< int>\"" -pkg=peds_testing -file=types_gen.go -imports github.com/tobgu/peds/tests/subpackage 12 | 13 | // Put vector in other package than type it contains 14 | //go:generate peds "-vectors=\"OtherVector\"" -pkg=subpackage2 -file=subpackage2/types_gen.go -imports github.com/tobgu/peds/tests/subpackage 15 | 16 | // go generate seems to require a function in the file that contains the generation expression... 17 | func f() { 18 | } 19 | -------------------------------------------------------------------------------- /tests/vector_test.go: -------------------------------------------------------------------------------- 1 | package peds_testing 2 | 3 | import ( 4 | "fmt" 5 | "github.com/tobgu/peds/tests/subpackage2" 6 | "runtime" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | /////////////// 12 | /// Helpers /// 13 | /////////////// 14 | 15 | func assertEqual(t *testing.T, expected, actual int) { 16 | if expected != actual { 17 | _, file, line, _ := runtime.Caller(1) 18 | t.Errorf("Error %s, line %d. Expected: %d, actual: %d", file, line, expected, actual) 19 | } 20 | } 21 | 22 | func assertEqualString(t *testing.T, expected, actual string) { 23 | if expected != actual { 24 | _, file, line, _ := runtime.Caller(1) 25 | t.Errorf("Error %s, line %d. Expected: %v, actual: %v", file, line, expected, actual) 26 | } 27 | } 28 | 29 | func assertEqualBool(t *testing.T, expected, actual bool) { 30 | if expected != actual { 31 | _, file, line, _ := runtime.Caller(1) 32 | t.Errorf("Error %s, line %d. Expected: %v, actual: %v", file, line, expected, actual) 33 | } 34 | } 35 | 36 | func assertPanic(t *testing.T, expectedMsg string) { 37 | if r := recover(); r == nil { 38 | _, _, line, _ := runtime.Caller(1) 39 | t.Errorf("Did not raise, line %d.", line) 40 | } else { 41 | msg := r.(string) 42 | if !strings.Contains(msg, expectedMsg) { 43 | t.Errorf("Msg '%s', did not contain '%s'", msg, expectedMsg) 44 | } 45 | } 46 | } 47 | 48 | func inputSlice(start, size int) []int { 49 | result := make([]int, 0, size) 50 | for i := start; i < start+size; i++ { 51 | result = append(result, i) 52 | } 53 | 54 | return result 55 | } 56 | 57 | var testSizes = []int{0, 1, 20, 32, 33, 50, 500, 32 * 32, 32*32 + 1, 10000, 32 * 32 * 32, 32*32*32 + 1} 58 | 59 | ////////////// 60 | /// Vector /// 61 | ////////////// 62 | 63 | func TestPropertiesOfNewVector(t *testing.T) { 64 | for _, l := range testSizes { 65 | t.Run(fmt.Sprintf("NewVector %d", l), func(t *testing.T) { 66 | vec := NewIntVector(inputSlice(0, l)...) 67 | assertEqual(t, vec.Len(), l) 68 | for i := 0; i < l; i++ { 69 | assertEqual(t, i, vec.Get(i)) 70 | } 71 | }) 72 | } 73 | } 74 | 75 | func TestSetItem(t *testing.T) { 76 | for _, l := range testSizes { 77 | t.Run(fmt.Sprintf("Set %d", l), func(t *testing.T) { 78 | vec := NewIntVector(inputSlice(0, l)...) 79 | for i := 0; i < l; i++ { 80 | newArr := vec.Set(i, -i) 81 | assertEqual(t, -i, newArr.Get(i)) 82 | assertEqual(t, i, vec.Get(i)) 83 | } 84 | }) 85 | } 86 | } 87 | 88 | func TestAppend(t *testing.T) { 89 | for _, l := range testSizes { 90 | vec := NewIntVector(inputSlice(0, l)...) 91 | t.Run(fmt.Sprintf("Append %d", l), func(t *testing.T) { 92 | for i := 0; i < 70; i++ { 93 | newVec := vec.Append(inputSlice(l, i)...) 94 | assertEqual(t, i+l, newVec.Len()) 95 | for j := 0; j < i+l; j++ { 96 | assertEqual(t, j, newVec.Get(j)) 97 | } 98 | 99 | // Original vector is unchanged 100 | assertEqual(t, l, vec.Len()) 101 | } 102 | }) 103 | } 104 | } 105 | 106 | func TestVectorSetOutOfBoundsNegative(t *testing.T) { 107 | defer assertPanic(t, "Index out of bounds") 108 | NewIntVector(inputSlice(0, 10)...).Set(-1, 0) 109 | } 110 | 111 | func TestVectorSetOutOfBoundsBeyondEnd(t *testing.T) { 112 | defer assertPanic(t, "Index out of bounds") 113 | NewIntVector(inputSlice(0, 10)...).Set(10, 0) 114 | } 115 | 116 | func TestVectorGetOutOfBoundsNegative(t *testing.T) { 117 | defer assertPanic(t, "Index out of bounds") 118 | NewIntVector(inputSlice(0, 10)...).Get(-1) 119 | } 120 | 121 | func TestVectorGetOutOfBoundsBeyondEnd(t *testing.T) { 122 | defer assertPanic(t, "Index out of bounds") 123 | NewIntVector(inputSlice(0, 10)...).Get(10) 124 | } 125 | 126 | func TestVectorSliceOutOfBounds(t *testing.T) { 127 | tests := []struct { 128 | start, stop int 129 | msg string 130 | }{ 131 | {-1, 3, "Invalid slice index"}, 132 | {0, 11, "Slice bounds out of range"}, 133 | {5, 3, "Invalid slice index"}, 134 | } 135 | 136 | for _, s := range tests { 137 | t.Run(fmt.Sprintf("start=%d, stop=%d", s.start, s.stop), func(t *testing.T) { 138 | defer assertPanic(t, s.msg) 139 | NewIntVector(inputSlice(0, 10)...).Slice(s.start, s.stop) 140 | }) 141 | } 142 | } 143 | 144 | func TestCompleteIteration(t *testing.T) { 145 | input := inputSlice(0, 10000) 146 | dst := make([]int, 0, 10000) 147 | NewIntVector(input...).Range(func(elem int) bool { 148 | dst = append(dst, elem) 149 | return true 150 | }) 151 | 152 | assertEqual(t, len(input), len(dst)) 153 | assertEqual(t, input[0], dst[0]) 154 | assertEqual(t, input[5000], dst[5000]) 155 | assertEqual(t, input[9999], dst[9999]) 156 | } 157 | 158 | func TestCanceledIteration(t *testing.T) { 159 | input := inputSlice(0, 10000) 160 | count := 0 161 | NewIntVector(input...).Range(func(elem int) bool { 162 | count++ 163 | if count == 5 { 164 | return false 165 | } 166 | return true 167 | }) 168 | 169 | assertEqual(t, 5, count) 170 | } 171 | 172 | ///////////// 173 | /// Slice /// 174 | ///////////// 175 | 176 | func TestSliceIndexes(t *testing.T) { 177 | vec := NewIntVector(inputSlice(0, 1000)...) 178 | slice := vec.Slice(0, 10) 179 | assertEqual(t, 1000, vec.Len()) 180 | assertEqual(t, 10, slice.Len()) 181 | assertEqual(t, 0, slice.Get(0)) 182 | assertEqual(t, 9, slice.Get(9)) 183 | 184 | // Slice of slice 185 | slice2 := slice.Slice(3, 7) 186 | assertEqual(t, 10, slice.Len()) 187 | assertEqual(t, 4, slice2.Len()) 188 | assertEqual(t, 3, slice2.Get(0)) 189 | assertEqual(t, 6, slice2.Get(3)) 190 | } 191 | 192 | func TestSliceCreation(t *testing.T) { 193 | sliceLen := 10000 194 | slice := NewIntVectorSlice(inputSlice(0, sliceLen)...) 195 | assertEqual(t, slice.Len(), sliceLen) 196 | for i := 0; i < sliceLen; i++ { 197 | assertEqual(t, i, slice.Get(i)) 198 | } 199 | } 200 | 201 | func TestSliceSet(t *testing.T) { 202 | vector := NewIntVector(inputSlice(0, 1000)...) 203 | slice := vector.Slice(10, 100) 204 | slice2 := slice.Set(5, 123) 205 | 206 | // Underlying vector and original slice should remain unchanged. New slice updated 207 | // in the correct position 208 | assertEqual(t, 15, vector.Get(15)) 209 | assertEqual(t, 15, slice.Get(5)) 210 | assertEqual(t, 123, slice2.Get(5)) 211 | } 212 | 213 | func TestSliceAppendInTheMiddleOfBackingVector(t *testing.T) { 214 | vector := NewIntVector(inputSlice(0, 100)...) 215 | slice := vector.Slice(0, 50) 216 | slice2 := slice.Append(inputSlice(0, 10)...) 217 | 218 | // New slice 219 | assertEqual(t, 60, slice2.Len()) 220 | assertEqual(t, 0, slice2.Get(50)) 221 | assertEqual(t, 9, slice2.Get(59)) 222 | 223 | // Original slice 224 | assertEqual(t, 50, slice.Len()) 225 | 226 | // Original vector 227 | assertEqual(t, 100, vector.Len()) 228 | assertEqual(t, 50, vector.Get(50)) 229 | assertEqual(t, 59, vector.Get(59)) 230 | } 231 | 232 | func TestSliceAppendAtTheEndOfBackingVector(t *testing.T) { 233 | vector := NewIntVector(inputSlice(0, 100)...) 234 | slice := vector.Slice(0, 100) 235 | slice2 := slice.Append(inputSlice(0, 10)...) 236 | 237 | // New slice 238 | assertEqual(t, 110, slice2.Len()) 239 | assertEqual(t, 0, slice2.Get(100)) 240 | assertEqual(t, 9, slice2.Get(109)) 241 | 242 | // Original slice 243 | assertEqual(t, 100, slice.Len()) 244 | } 245 | 246 | func TestSliceAppendAtMiddleToEndOfBackingVector(t *testing.T) { 247 | vector := NewIntVector(inputSlice(0, 100)...) 248 | slice := vector.Slice(0, 50) 249 | slice2 := slice.Append(inputSlice(0, 100)...) 250 | 251 | // New slice 252 | assertEqual(t, 150, slice2.Len()) 253 | assertEqual(t, 0, slice2.Get(50)) 254 | assertEqual(t, 99, slice2.Get(149)) 255 | 256 | // Original slice 257 | assertEqual(t, 50, slice.Len()) 258 | } 259 | 260 | func TestSliceCompleteIteration(t *testing.T) { 261 | vec := NewIntVector(inputSlice(0, 1000)...) 262 | dst := make([]int, 0) 263 | 264 | vec.Slice(5, 200).Range(func(elem int) bool { 265 | dst = append(dst, elem) 266 | return true 267 | }) 268 | 269 | assertEqual(t, 195, len(dst)) 270 | assertEqual(t, 5, dst[0]) 271 | assertEqual(t, 55, dst[50]) 272 | assertEqual(t, 199, dst[194]) 273 | } 274 | 275 | func TestSliceCanceledIteration(t *testing.T) { 276 | vec := NewIntVector(inputSlice(0, 1000)...) 277 | count := 0 278 | 279 | vec.Slice(5, 200).Range(func(elem int) bool { 280 | count++ 281 | if count == 5 { 282 | return false 283 | } 284 | 285 | return true 286 | }) 287 | 288 | assertEqual(t, 5, count) 289 | } 290 | 291 | func TestSliceSetOutOfBoundsNegative(t *testing.T) { 292 | defer assertPanic(t, "Index out of bounds") 293 | NewIntVector(inputSlice(0, 10)...).Slice(2, 5).Set(-1, 0) 294 | } 295 | 296 | func TestSliceSetOutOfBoundsBeyondEnd(t *testing.T) { 297 | defer assertPanic(t, "Index out of bounds") 298 | NewIntVector(inputSlice(0, 10)...).Slice(2, 5).Set(4, 0) 299 | } 300 | 301 | func TestSliceGetOutOfBoundsNegative(t *testing.T) { 302 | defer assertPanic(t, "Index out of bounds") 303 | NewIntVector(inputSlice(0, 10)...).Slice(2, 5).Get(-1) 304 | } 305 | 306 | func TestSliceGetOutOfBoundsBeyondEnd(t *testing.T) { 307 | defer assertPanic(t, "Index out of bounds") 308 | NewIntVector(inputSlice(0, 10)...).Slice(2, 5).Get(4) 309 | } 310 | 311 | func TestSliceSliceOutOfBounds(t *testing.T) { 312 | tests := []struct { 313 | start, stop int 314 | msg string 315 | }{ 316 | {-1, 3, "Invalid slice index"}, 317 | {0, 4, "Slice bounds out of range"}, 318 | {3, 2, "Invalid slice index"}, 319 | } 320 | 321 | for _, s := range tests { 322 | t.Run(fmt.Sprintf("start=%d, stop=%d", s.start, s.stop), func(t *testing.T) { 323 | defer assertPanic(t, s.msg) 324 | NewIntVector(inputSlice(0, 10)...).Slice(2, 5).Slice(s.start, s.stop) 325 | }) 326 | } 327 | } 328 | 329 | func TestTypesFromOtherPackage(t *testing.T) { 330 | v := NewImportVector(1, 2, 3) 331 | assertEqual(t, 3, v.Len()) 332 | } 333 | 334 | func TestVectorInOtherPackage(t *testing.T) { 335 | v := subpackage2.NewOtherVector(1, 2, 3) 336 | assertEqual(t, 3, v.Len()) 337 | } 338 | 339 | func TestToNativeVector(t *testing.T) { 340 | lengths := []int{0, 1, 7, 32, 512, 1000} 341 | for _, length := range lengths { 342 | t.Run(fmt.Sprintf("length=%d", length), func(t *testing.T) { 343 | inputS := inputSlice(0, length) 344 | v := NewIntVector(inputS...) 345 | 346 | outputS := v.ToNativeSlice() 347 | 348 | assertEqual(t, len(inputS), len(outputS)) 349 | for i := range outputS { 350 | assertEqual(t, inputS[i], outputS[i]) 351 | } 352 | }) 353 | } 354 | } 355 | 356 | // TODO: 357 | // - Document public types and methods 358 | // - Expand README with examples 359 | 360 | ////////////////////// 361 | ///// Benchmarks ///// 362 | ////////////////////// 363 | 364 | // Used to avoid that the compiler optimizes the code away 365 | var result int 366 | 367 | func runIteration(b *testing.B, size int) { 368 | vec := NewIntVector(inputSlice(0, size)...) 369 | b.ResetTimer() 370 | for n := 0; n < b.N; n++ { 371 | vec.Range(func(x int) bool { 372 | result += x 373 | return true 374 | }) 375 | } 376 | } 377 | 378 | func BenchmarkLargeIteration(b *testing.B) { 379 | runIteration(b, 100000) 380 | } 381 | 382 | func BenchmarkSmallIteration(b *testing.B) { 383 | runIteration(b, 10) 384 | } 385 | -------------------------------------------------------------------------------- /util/create_templates.py: -------------------------------------------------------------------------------- 1 | # Basic script to generate go text template strings from container code. 2 | # Compatible with Python 2 and 3. Only relies on stdlib. 3 | 4 | if __name__ == "__main__": 5 | template_start_mark = '//template:' 6 | input_file_name = 'internal/generic_types/containers.go' 7 | output_file_name = 'internal/templates/templates.go' 8 | template_package_name = 'templates' 9 | generic_types = {'GenericVectorType': 'VectorTypeName', 10 | 'GenericType': 'TypeName', 11 | 'GenericMapType': 'MapTypeName', 12 | 'GenericMapItem': 'MapItemTypeName', 13 | 'GenericMapKeyType': 'MapKeyTypeName', 14 | 'GenericMapValueType': 'MapValueTypeName', 15 | 'genericHash': 'MapKeyHashFunc', 16 | 'GenericSetType': 'SetTypeName'} 17 | 18 | state = 'searching' 19 | template_name = '' 20 | template = '' 21 | templates = {} 22 | 23 | print("Generating templates") 24 | with open(input_file_name, 'r') as input_file: 25 | for line in input_file: 26 | if line.startswith(template_start_mark): 27 | if state == 'reading': 28 | templates[template_name] = template 29 | 30 | template_name = line[len(template_start_mark):].strip() 31 | template = '' 32 | state = 'reading' 33 | else: 34 | if state == 'reading': 35 | template += line 36 | 37 | if state == 'reading': 38 | templates[template_name] = template 39 | 40 | with open(output_file_name, 'w') as output_file: 41 | output_file.write('package {}\n\n'.format(template_package_name)) 42 | output_file.write("// NOTE: This file is auto generated, don't edit manually!\n".format(template_package_name)) 43 | for name, template in sorted(list(templates.items())): 44 | for generic_type, variable_name in generic_types.items(): 45 | template = template.replace(generic_type, '{{{{.{variable_name}}}}}'.format(variable_name=variable_name)) 46 | output_file.write('const {name} string = `'.format(name=name)) 47 | output_file.write(template) 48 | output_file.write('`\n') 49 | 50 | print("Done!") 51 | --------------------------------------------------------------------------------