Skip to content

Commit 007cda6

Browse files
committed
compact: add Make methods for compact ranges
The New* methods unnecessarily allocate. If the user needs a pointer, they can convert, but most users only need it by value.
1 parent c5d6d85 commit 007cda6

File tree

1 file changed

+49
-7
lines changed

1 file changed

+49
-7
lines changed

compact/range.go

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,53 @@ type RangeFactory struct {
3434
Hash HashFn
3535
}
3636

37-
// NewRange creates a Range for [begin, end) with the given set of hashes. The
37+
// MakeRange creates a Range for [begin, end) with the given set of hashes. The
3838
// hashes correspond to the roots of the minimal set of perfect sub-trees
3939
// covering the [begin, end) leaves range, ordered left to right.
40-
func (f *RangeFactory) NewRange(begin, end uint64, hashes [][]byte) (*Range, error) {
40+
//
41+
// See the Hazard in the Range comment to avoid pitfalls.
42+
func (f *RangeFactory) MakeRange(begin, end uint64, hashes [][]byte) (Range, error) {
4143
if end < begin {
42-
return nil, fmt.Errorf("invalid range: end=%d, want >= %d", end, begin)
44+
return Range{}, fmt.Errorf("invalid range: end=%d, want >= %d", end, begin)
4345
}
4446
if got, want := len(hashes), RangeSize(begin, end); got != want {
45-
return nil, fmt.Errorf("invalid hashes: got %d values, want %d", got, want)
47+
return Range{}, fmt.Errorf("invalid hashes: got %d values, want %d", got, want)
48+
}
49+
return Range{f: f, begin: begin, end: end, hashes: hashes}, nil
50+
}
51+
52+
// NewRange creates a Range for [begin, end) with the given set of hashes. The
53+
// hashes correspond to the roots of the minimal set of perfect sub-trees
54+
// covering the [begin, end) leaves range, ordered left to right.
55+
//
56+
// It is recommended to use MakeRange instead, which does not allocate.
57+
// If in doubt, NewRange is safer.
58+
func (f *RangeFactory) NewRange(begin, end uint64, hashes [][]byte) (*Range, error) {
59+
r, err := f.MakeRange(begin, end, hashes)
60+
if err != nil {
61+
return nil, err
4662
}
47-
return &Range{f: f, begin: begin, end: end, hashes: hashes}, nil
63+
return &r, nil
64+
}
65+
66+
// MakeEmptyRange returns a new Range for an empty [begin, begin) range. The
67+
// value of begin defines where the range will start growing from when entries
68+
// are appended to it.
69+
//
70+
// See the Hazard in the Range comment to avoid pitfalls.
71+
func (f *RangeFactory) MakeEmptyRange(begin uint64) Range {
72+
return Range{f: f, begin: begin, end: begin}
4873
}
4974

5075
// NewEmptyRange returns a new Range for an empty [begin, begin) range. The
5176
// value of begin defines where the range will start growing from when entries
5277
// are appended to it.
78+
//
79+
// It is recommended to use MakeEmptyRange instead, which does not allocate.
80+
// If in doubt, NewEmptyRange is safer.
5381
func (f *RangeFactory) NewEmptyRange(begin uint64) *Range {
54-
return &Range{f: f, begin: begin, end: begin}
82+
r := f.MakeEmptyRange(begin)
83+
return &r
5584
}
5685

5786
// Range represents a compact Merkle tree range for leaf indices [begin, end).
@@ -60,8 +89,21 @@ func (f *RangeFactory) NewEmptyRange(begin uint64) *Range {
6089
// range. The structure is efficiently mergeable with other compact ranges that
6190
// share one of the endpoints with it.
6291
//
63-
// For more details, see
92+
// For more details on compact ranges and how they can be used, see
6493
// https://github.com/transparency-dev/merkle/blob/main/docs/compact_ranges.md.
94+
//
95+
// Hazard: the Range is mutable, and its internal slice can be updated in-place
96+
// by Append* methods.
97+
//
98+
// cr := factory.MakeRange(0, 10, hashes)
99+
// populate(cr, leaves) // internally calls cr.Append()
100+
// use(cr) // hazard: cr could be corrupted by populate()
101+
//
102+
// To be safe, the Range should be used by pointer. When using by value, be
103+
// sure that it is either semantically immutable, or the value is updated after
104+
// mutations (e.g., in the example above, the updated Range is returned from
105+
// the populate() function and assigned to cr). The latter is a common pattern
106+
// in Go, think slices: slice = append(slice, value).
65107
type Range struct {
66108
f *RangeFactory
67109
begin uint64

0 commit comments

Comments
 (0)