commit 76a1f7b0a3e3380ebd38e46cf4b8d9cc59d9c3ac
parent ea1110f7896f0bb6909e9acce2b6eb720f25979c
Author: Jack Mordaunt <jackmordaunt@gmail.com>
Date: Thu, 5 Nov 2020 13:44:06 +0800
doc: explain map type
Signed-off-by: Jack Mordaunt <jackmordaunt@gmail.com>
Diffstat:
1 file changed, 48 insertions(+), 11 deletions(-)
diff --git a/cmd/kanban/map.go b/cmd/kanban/map.go
@@ -2,30 +2,67 @@ package main
import "unsafe"
-// Map of arbitrary data.
+// Map of arbitrary data to hold unorded state for `layout.List` items.
+// This allows Gio programs to re-use a buffer of states for lists items in
+// between frames. It is a grow-only buffer that expects entries to stabilise.
+//
+// This is designed along 2 constraints:
+// 1. Performance
+// 2. Type ambiguity
+//
+// Since Go doesn't have generics, I decided to give the caller type control
+// by using `unsafe.Pointer`.
+//
+// The caller only has to ensure that the type they initialise it with is the
+// type they attempt to cast out of it.
+// Since the scope of use is small, this invariant is straighforward to uphold.
+//
+// Nonetheless, this style of API is primarily motived by re-use concerns when
+// using common patterns in Gio (specifically `layout.List` state management).
+// The static approach would be to copy-paste the same structures with different
+// types everytime you have list state to manage.
+//
+// In light of Go generics incoming, this may become a moot issue. In the meantime
+// this remains an experimental API that functions as expected.
type Map struct {
- data map[string]unsafe.Pointer
+ data map[string]unsafe.Pointer
+ index []string
+ current int
}
+// Begin prepares the map to be accessed.
+// Require to reset iteration state each frame.
func (m *Map) Begin() {
+ m.current = 0
if m.data == nil {
m.data = make(map[string]unsafe.Pointer)
}
}
-func (m *Map) Next(k string, v unsafe.Pointer) unsafe.Pointer {
+// New returns a value for the provided key.
+// In the case no value exists, the initializer is used as the default value.
+//
+// v := (*T)(m.New("foo", &T{}))
+//
+func (m *Map) New(k string, init unsafe.Pointer) unsafe.Pointer {
if _, ok := m.data[k]; !ok {
- m.data[k] = v
+ m.data[k] = init
+ m.index = append(m.index, k)
}
return m.data[k]
}
-func (m *Map) List() []unsafe.Pointer {
- list := []unsafe.Pointer{}
- for _, v := range m.data {
- if v != nil {
- list = append(list, v)
- }
+// Next iterates over the collect, returning the data and a boolean to indicate
+// the end.
+//
+// for v, ok := m.Next(); ok; v, ok = m.Next() {
+// t := (*T)(v)
+// }
+//
+func (m *Map) Next() (unsafe.Pointer, bool) {
+ if m.current >= len(m.index) {
+ return nil, false
}
- return list
+ defer func() { m.current++ }()
+ return m.data[m.index[m.current]], m.current <= len(m.index)-1
}