From 52c37f354f2d14bf06f7402e9607191156a8edec Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Mon, 31 May 2021 19:49:50 +0000 Subject: [PATCH] Add tools/src/list: A dynamic typed list Will be used by the lookup table package, which is used by the intrinsic definition generator. Bug: tint:832 Change-Id: I72c2dc5e37678dbaffc1a32e1631caa8ba6c690e Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/52600 Commit-Queue: Ben Clayton Reviewed-by: David Neto --- tools/src/list/list.go | 137 ++++++++++++++++++++++ tools/src/list/list_test.go | 227 ++++++++++++++++++++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 tools/src/list/list.go create mode 100644 tools/src/list/list_test.go diff --git a/tools/src/list/list.go b/tools/src/list/list.go new file mode 100644 index 0000000000..3c89ba7152 --- /dev/null +++ b/tools/src/list/list.go @@ -0,0 +1,137 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package list provides utilities for handling lists of dynamically-typed elements +package list + +import ( + "fmt" + "reflect" +) + +// List is an interface to a list of dynamically-typed elements +type List interface { + // Count returns the number if items in the list + Count() int + + // Get returns the element at the index i + Get(i int) interface{} + + // Set assigns the element at the index i with v + Set(i int, v interface{}) + + // Append adds a single item, list, or slice of items to this List + Append(v interface{}) + + // Copy copies the elements at [dst..dst+count) to [src..src+count) + Copy(dst, src, count int) + + // CopyFrom copies the elements [src..src+count) from the list l to the + // elements [dst..dst+count) in this list + CopyFrom(l List, dst, src, count int) + + // Reduces the size of the list to count elements + Resize(count int) + + // ElementType returns the type of the elements of the list + ElementType() reflect.Type +} + +// Wrap returns a List that wraps a slice pointer +func Wrap(s interface{}) List { + ptr := reflect.ValueOf(s) + if ptr.Kind() != reflect.Ptr || ptr.Elem().Kind() != reflect.Slice { + panic(fmt.Errorf("Wrap() must be called with a pointer to slice. Got: %T", s)) + } + return list{ptr.Elem()} +} + +// New returns a new list of element type elem for n items +func New(elem reflect.Type, count int) List { + slice := reflect.SliceOf(elem) + return list{reflect.MakeSlice(slice, count, count)} +} + +// Copy makes a shallow copy of the list +func Copy(l List) List { + out := New(l.ElementType(), l.Count()) + out.CopyFrom(l, 0, 0, l.Count()) + return out +} + +type list struct{ v reflect.Value } + +func (l list) Count() int { + return l.v.Len() +} + +func (l list) Get(i int) interface{} { + return l.v.Index(i).Interface() +} + +func (l list) Set(i int, v interface{}) { + l.v.Index(i).Set(reflect.ValueOf(v)) +} + +func (l list) Append(v interface{}) { + switch v := v.(type) { + case list: + l.v.Set(reflect.AppendSlice(l.v, reflect.Value(v.v))) + case List: + // v implements `List`, but isn't a `list`. Need to do a piece-wise copy + items := make([]reflect.Value, v.Count()) + for i := range items { + items[i] = reflect.ValueOf(v.Get(i)) + } + l.v.Set(reflect.Append(l.v, items...)) + default: + r := reflect.ValueOf(v) + if r.Type() == l.v.Type() { + l.v.Set(reflect.AppendSlice(l.v, r)) + return + } + l.v.Set(reflect.Append(l.v, reflect.ValueOf(v))) + } +} + +func (l list) Copy(dst, src, count int) { + reflect.Copy( + l.v.Slice(dst, dst+count), + l.v.Slice(src, src+count), + ) +} + +func (l list) CopyFrom(o List, dst, src, count int) { + if o, ok := o.(list); ok { + reflect.Copy( + l.v.Slice(dst, dst+count), + o.v.Slice(src, src+count), + ) + } + // v implements `List`, but isn't a `list`. Need to do a piece-wise copy + items := make([]reflect.Value, count) + for i := range items { + l.Set(dst+i, o.Get(src+i)) + } +} + +func (l list) Resize(count int) { + new := reflect.MakeSlice(l.v.Type(), count, count) + reflect.Copy(new, l.v) + l.v.Set(new) +} + +func (l list) ElementType() reflect.Type { + return l.v.Type().Elem() +} diff --git a/tools/src/list/list_test.go b/tools/src/list/list_test.go new file mode 100644 index 0000000000..04503ede4d --- /dev/null +++ b/tools/src/list/list_test.go @@ -0,0 +1,227 @@ +// Copyright 2021 The Tint Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either exprel or implied. +// See the License for the specific language governing permilions and +// limitations under the License. + +package list_test + +import ( + "reflect" + "testing" + + "dawn.googlesource.com/tint/tools/src/list" +) + +// A simple implementation of list.List. Many methods are just stubs +type customList struct{} + +func (customList) Count() int { return 3 } +func (customList) Get(i int) interface{} { return 10 + i*10 } +func (customList) Set(i int, v interface{}) {} +func (customList) Append(v interface{}) {} +func (customList) Copy(dst, src, count int) {} +func (customList) CopyFrom(l list.List, dst, src, count int) {} +func (customList) Resize(count int) {} +func (customList) ElementType() reflect.Type { return nil } + +var _ list.List = customList{} // Interface compliance check + +func TestNew(t *testing.T) { + l := list.New(reflect.TypeOf(0), 3) + + if n := l.Count(); n != 3 { + t.Errorf("Count(0): %v", n) + } + if n := l.Get(0); n != 0 { + t.Errorf("Get(0): %v", n) + } + if n := l.Get(1); n != 0 { + t.Errorf("Get(1): %v", n) + } + if n := l.Get(2); n != 0 { + t.Errorf("Get(2): %v", n) + } +} + +func TestCopy(t *testing.T) { + slice := []int{1, 2, 3} + l := list.Wrap(&slice) + + c := list.Copy(l) + + if n := c.Count(); n != 3 { + t.Errorf("Count(0): %v", n) + } + if n := c.Get(0); n != 1 { + t.Errorf("Get(0): %v", n) + } + if n := c.Get(1); n != 2 { + t.Errorf("Get(1): %v", n) + } + if n := c.Get(2); n != 3 { + t.Errorf("Get(2): %v", n) + } +} + +func TestListCount(t *testing.T) { + slice := make([]int, 5) + l := list.Wrap(&slice) + + if c := l.Count(); c != 5 { + t.Errorf("Count() is %v", c) + } +} + +func TestListGrow(t *testing.T) { + slice := []int{} + l := list.Wrap(&slice) + + l.Resize(10) + + if len(slice) != 10 { + t.Errorf("len(slice) after Resize(10) is %v", len(slice)) + } +} + +func TestListShrink(t *testing.T) { + slice := make([]int, 10) + l := list.Wrap(&slice) + + l.Resize(5) + + if len(slice) != 5 { + t.Errorf("len(slice) after Resize(5) is %v", len(slice)) + } +} + +func TestListCopy(t *testing.T) { + slice := []int{0, 10, 20, 0, 0, 0} + l := list.Wrap(&slice) + + l.Copy(3, 1, 2) + + if !reflect.DeepEqual(slice, []int{0, 10, 20, 10, 20, 0}) { + t.Errorf("after Copy(), slice: %v", slice) + } +} + +func TestListCopyFromList(t *testing.T) { + sliceA := []int{10, 20, 30, 40, 50, 60} + lA := list.Wrap(&sliceA) + + sliceB := []int{1, 2, 3, 4, 5, 6} + lB := list.Wrap(&sliceB) + + lA.CopyFrom(lB, 1, 2, 3) + + if !reflect.DeepEqual(sliceA, []int{10, 3, 4, 5, 50, 60}) { + t.Errorf("after CopyFrom(), slice: %v", sliceA) + } +} + +func TestListCopyFromCustomList(t *testing.T) { + sliceA := []int{10, 20, 30, 40, 50, 60} + lA := list.Wrap(&sliceA) + + lA.CopyFrom(customList{}, 1, 2, 3) + + if !reflect.DeepEqual(sliceA, []int{10, 30, 40, 50, 50, 60}) { + t.Errorf("after CopyFrom(), slice: %v", sliceA) + } +} + +func TestListGet(t *testing.T) { + slice := []int{0, 10, 20, 10, 20} + l := list.Wrap(&slice) + + if n := l.Get(0); n != 0 { + t.Errorf("Get(0): %v", n) + } + if n := l.Get(1); n != 10 { + t.Errorf("Get(1): %v", n) + } + if n := l.Get(2); n != 20 { + t.Errorf("Get(2): %v", n) + } + if n := l.Get(3); n != 10 { + t.Errorf("Get(3): %v", n) + } + if n := l.Get(4); n != 20 { + t.Errorf("Get(4): %v", n) + } +} + +func TestListSet(t *testing.T) { + slice := []int{0, 10, 20, 10, 20} + l := list.Wrap(&slice) + + l.Set(0, 50) + l.Set(2, 90) + l.Set(4, 60) + + if !reflect.DeepEqual(slice, []int{50, 10, 90, 10, 60}) { + t.Errorf("after Set(), slice: %v", slice) + } +} + +func TestListAppendItem(t *testing.T) { + slice := []int{1, 2, 3} + l := list.Wrap(&slice) + + l.Append(9) + + if c := len(slice); c != 4 { + t.Errorf("len(slice): %v", 4) + } + if n := slice[3]; n != 9 { + t.Errorf("slice[3]: %v", n) + } +} + +func TestListAppendItems(t *testing.T) { + slice := []int{1, 2, 3} + l := list.Wrap(&slice) + + l.Append([]int{9, 8, 7}) + + if !reflect.DeepEqual(slice, []int{1, 2, 3, 9, 8, 7}) { + t.Errorf("after Append(), slice: %v", slice) + } +} + +func TestListAppendList(t *testing.T) { + sliceA := []int{1, 2, 3} + lA := list.Wrap(&sliceA) + + sliceB := []int{9, 8, 7} + lB := list.Wrap(&sliceB) + + lA.Append(lB) + + if !reflect.DeepEqual(sliceA, []int{1, 2, 3, 9, 8, 7}) { + t.Errorf("after Append(), sliceA: %v", sliceA) + } + if !reflect.DeepEqual(sliceB, []int{9, 8, 7}) { + t.Errorf("after Append(), sliceB: %v", sliceB) + } +} + +func TestListAppendCustomList(t *testing.T) { + sliceA := []int{1, 2, 3} + lA := list.Wrap(&sliceA) + + lA.Append(customList{}) + + if !reflect.DeepEqual(sliceA, []int{1, 2, 3, 10, 20, 30}) { + t.Errorf("after Append(), sliceA: %v", sliceA) + } +}