Skip to content

Commit

Permalink
Merge pull request #16 from benbjohnson/refactor-builders
Browse files Browse the repository at this point in the history
Refactor builders
  • Loading branch information
benbjohnson authored Nov 17, 2020
2 parents 571d514 + 0a4e560 commit 065fdb4
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 108 deletions.
8 changes: 3 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ a list in-place until you are ready to use it. This can improve bulk list
building by 10x or more.

```go
b := immutable.NewListBuilder(immutable.NewList())
b := immutable.NewListBuilder()
b.Append("foo")
b.Append("bar")
b.Set(2, "baz")
Expand All @@ -125,8 +125,7 @@ fmt.Println(l.Get(0)) // "foo"
fmt.Println(l.Get(1)) // "baz"
```

Lists are safe to use even after you continue to use the builder. It is also
safe to build on top of existing lists.
Builders are invalid after the call to `List()`.


## Map
Expand Down Expand Up @@ -230,8 +229,7 @@ fmt.Println(m.Get("foo")) // "300"
fmt.Println(m.Get("bar")) // "200"
```

Maps are safe to use even after you continue to use the builder. You can
also build on top of existing maps too.
Builders are invalid after the call to `Map()`.


### Implementing a custom Hasher
Expand Down
118 changes: 68 additions & 50 deletions immutable.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,65 +224,69 @@ func (l *List) Iterator() *ListIterator {
return itr
}

// ListBuilder represents an efficient builder for creating Lists.
//
// Lists returned from the builder are safe to use even after you continue to
// use the builder. However, for efficiency, you should only retrieve your list
// after you have completed building it.
// ListBuilder represents an efficient builder for creating new Lists.
type ListBuilder struct {
list *List // current state
mutable bool // if true, next mutation will operate in-place.
list *List // current state
}

// NewListBuilder returns a new instance of ListBuilder to build on a base list.
func NewListBuilder(list *List) *ListBuilder {
return &ListBuilder{list: list}
// NewListBuilder returns a new instance of ListBuilder.
func NewListBuilder() *ListBuilder {
return &ListBuilder{list: NewList()}
}

// List returns the current copy of the list.
// The returned list is safe to use even if after the builder continues to be used.
// The builder should not be used again after the list after this call.
func (b *ListBuilder) List() *List {
assert(b.list != nil, "immutable.ListBuilder.List(): duplicate call to fetch list")
list := b.list
b.mutable = false
b.list = nil
return list
}

// Len returns the number of elements in the underlying list.
func (b *ListBuilder) Len() int {
assert(b.list != nil, "immutable.ListBuilder: builder invalid after List() invocation")
return b.list.Len()
}

// Get returns the value at the given index. Similar to slices, this method will
// panic if index is below zero or is greater than or equal to the list size.
func (b *ListBuilder) Get(index int) interface{} {
assert(b.list != nil, "immutable.ListBuilder: builder invalid after List() invocation")
return b.list.Get(index)
}

// Set updates the value at the given index. Similar to slices, this method will
// panic if index is below zero or if the index is greater than or equal to the
// list size.
func (b *ListBuilder) Set(index int, value interface{}) {
b.list = b.list.set(index, value, b.mutable)
b.mutable = true
assert(b.list != nil, "immutable.ListBuilder: builder invalid after List() invocation")
b.list = b.list.set(index, value, true)
}

// Append adds value to the end of the list.
func (b *ListBuilder) Append(value interface{}) {
b.list = b.list.append(value, b.mutable)
b.mutable = true
assert(b.list != nil, "immutable.ListBuilder: builder invalid after List() invocation")
b.list = b.list.append(value, true)
}

// Prepend adds value to the beginning of the list.
func (b *ListBuilder) Prepend(value interface{}) {
b.list = b.list.prepend(value, b.mutable)
b.mutable = true
assert(b.list != nil, "immutable.ListBuilder: builder invalid after List() invocation")
b.list = b.list.prepend(value, true)
}

// Slice updates the list with a sublist of elements between start and end index.
// See List.Slice() for more details.
func (b *ListBuilder) Slice(start, end int) {
b.list = b.list.slice(start, end, b.mutable)
b.mutable = true
assert(b.list != nil, "immutable.ListBuilder: builder invalid after List() invocation")
b.list = b.list.slice(start, end, true)
}

// Iterator returns a new iterator for the underlying list.
func (b *ListBuilder) Iterator() *ListIterator {
assert(b.list != nil, "immutable.ListBuilder: builder invalid after List() invocation")
return b.list.Iterator()
}

// Constants for bit shifts used for levels in the List trie.
Expand Down Expand Up @@ -788,48 +792,52 @@ func (m *Map) Iterator() *MapIterator {
}

// MapBuilder represents an efficient builder for creating Maps.
//
// Maps returned from the builder are safe to use even after you continue to
// use the builder. However, for efficiency, you should only retrieve your map
// after you have completed building it.
type MapBuilder struct {
m *Map // current state
mutable bool // if true, next mutation will operate in-place.
m *Map // current state
}

// NewMapBuilder returns a new instance of MapBuilder to build on a base map.
func NewMapBuilder(m *Map) *MapBuilder {
return &MapBuilder{m: m}
// NewMapBuilder returns a new instance of MapBuilder.
func NewMapBuilder(hasher Hasher) *MapBuilder {
return &MapBuilder{m: NewMap(hasher)}
}

// Map returns the current copy of the map.
// The returned map is safe to use even if after the builder continues to be used.
// Map returns the underlying map. Only call once.
// Builder is invalid after call. Will panic on second invocation.
func (b *MapBuilder) Map() *Map {
assert(b.m != nil, "immutable.SortedMapBuilder.Map(): duplicate call to fetch map")
m := b.m
b.mutable = false
b.m = nil
return m
}

// Len returns the number of elements in the underlying map.
func (b *MapBuilder) Len() int {
assert(b.m != nil, "immutable.MapBuilder: builder invalid after Map() invocation")
return b.m.Len()
}

// Get returns the value for the given key.
func (b *MapBuilder) Get(key interface{}) (value interface{}, ok bool) {
assert(b.m != nil, "immutable.MapBuilder: builder invalid after Map() invocation")
return b.m.Get(key)
}

// Set sets the value of the given key. See Map.Set() for additional details.
func (b *MapBuilder) Set(key, value interface{}) {
b.m = b.m.set(key, value, b.mutable)
b.mutable = true
assert(b.m != nil, "immutable.MapBuilder: builder invalid after Map() invocation")
b.m = b.m.set(key, value, true)
}

// Delete removes the given key. See Map.Delete() for additional details.
func (b *MapBuilder) Delete(key interface{}) {
b.m = b.m.delete(key, b.mutable)
b.mutable = true
assert(b.m != nil, "immutable.MapBuilder: builder invalid after Map() invocation")
b.m = b.m.delete(key, true)
}

// Iterator returns a new iterator for the underlying map.
func (b *MapBuilder) Iterator() *MapIterator {
assert(b.m != nil, "immutable.MapBuilder: builder invalid after Map() invocation")
return b.m.Iterator()
}

// mapNode represents any node in the map tree.
Expand Down Expand Up @@ -1660,48 +1668,52 @@ func (m *SortedMap) Iterator() *SortedMapIterator {
}

// SortedMapBuilder represents an efficient builder for creating sorted maps.
//
// Maps returned from the builder are safe to use even after you continue to
// use the builder. However, for efficiency, you should only retrieve your map
// after you have completed building it.
type SortedMapBuilder struct {
m *SortedMap // current state
mutable bool // if true, next mutation will operate in-place.
m *SortedMap // current state
}

// NewSortedMapBuilder returns a new instance of SortedMapBuilder to build on a base map.
func NewSortedMapBuilder(m *SortedMap) *SortedMapBuilder {
return &SortedMapBuilder{m: m}
// NewSortedMapBuilder returns a new instance of SortedMapBuilder.
func NewSortedMapBuilder(comparer Comparer) *SortedMapBuilder {
return &SortedMapBuilder{m: NewSortedMap(comparer)}
}

// SortedMap returns the current copy of the map.
// The returned map is safe to use even if after the builder continues to be used.
func (b *SortedMapBuilder) Map() *SortedMap {
assert(b.m != nil, "immutable.SortedMapBuilder.Map(): duplicate call to fetch map")
m := b.m
b.mutable = false
b.m = nil
return m
}

// Len returns the number of elements in the underlying map.
func (b *SortedMapBuilder) Len() int {
assert(b.m != nil, "immutable.SortedMapBuilder: builder invalid after Map() invocation")
return b.m.Len()
}

// Get returns the value for the given key.
func (b *SortedMapBuilder) Get(key interface{}) (value interface{}, ok bool) {
assert(b.m != nil, "immutable.SortedMapBuilder: builder invalid after Map() invocation")
return b.m.Get(key)
}

// Set sets the value of the given key. See SortedMap.Set() for additional details.
func (b *SortedMapBuilder) Set(key, value interface{}) {
b.m = b.m.set(key, value, b.mutable)
b.mutable = true
assert(b.m != nil, "immutable.SortedMapBuilder: builder invalid after Map() invocation")
b.m = b.m.set(key, value, true)
}

// Delete removes the given key. See SortedMap.Delete() for additional details.
func (b *SortedMapBuilder) Delete(key interface{}) {
b.m = b.m.delete(key, b.mutable)
b.mutable = true
assert(b.m != nil, "immutable.SortedMapBuilder: builder invalid after Map() invocation")
b.m = b.m.delete(key, true)
}

// Iterator returns a new iterator for the underlying map positioned at the first key.
func (b *SortedMapBuilder) Iterator() *SortedMapIterator {
assert(b.m != nil, "immutable.SortedMapBuilder: builder invalid after Map() invocation")
return b.m.Iterator()
}

// sortedMapNode represents a branch or leaf node in the sorted map.
Expand Down Expand Up @@ -2714,3 +2726,9 @@ type reflectStringComparer struct{}
func (c *reflectStringComparer) Compare(a, b interface{}) int {
return strings.Compare(reflect.ValueOf(a).String(), reflect.ValueOf(b).String())
}

func assert(condition bool, message string) {
if !condition {
panic(message)
}
}
Loading

0 comments on commit 065fdb4

Please sign in to comment.