diff --git a/common/range.go b/common/range.go new file mode 100644 index 0000000000..ca7af1b56d --- /dev/null +++ b/common/range.go @@ -0,0 +1,116 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "io" + "iter" + + "github.com/ethereum/go-ethereum/rlp" +) + +// Range represents a range of integers. +type Range[T uint32 | uint64] struct { + first, afterLast T +} + +func NewRange[T uint32 | uint64](first, count T) Range[T] { + return Range[T]{first, first + count} +} + +func (r *Range[T]) EncodeRLP(w io.Writer) error { + if err := rlp.Encode(w, &r.first); err != nil { + return err + } + return rlp.Encode(w, &r.afterLast) +} + +func (r *Range[T]) DecodeRLP(s *rlp.Stream) error { + if err := s.Decode(&r.first); err != nil { + return err + } + return s.Decode(&r.afterLast) +} + +func (r Range[T]) First() T { + return r.first +} + +func (r Range[T]) Last() T { + if r.first == r.afterLast { + panic("last item of zero length range is not allowed") + } + return r.afterLast - 1 +} + +func (r Range[T]) AfterLast() T { + return r.afterLast +} + +func (r Range[T]) Count() T { + return r.afterLast - r.first +} + +func (r Range[T]) IsEmpty() bool { + return r.first == r.afterLast +} + +func (r Range[T]) Includes(v T) bool { + return v >= r.first && v < r.afterLast +} + +func (r *Range[T]) SetFirst(v T) { + r.first = v + if r.afterLast < r.first { + r.afterLast = r.first + } +} + +func (r *Range[T]) SetAfterLast(v T) { + r.afterLast = v + if r.afterLast < r.first { + r.first = r.afterLast + } +} + +func (r *Range[T]) SetLast(v T) { + r.SetAfterLast(v + 1) +} + +func (r Range[T]) Intersection(q Range[T]) Range[T] { + if r.first > q.first { + q.first = r.first + } + if r.afterLast < q.afterLast { + q.afterLast = r.afterLast + } + if q.first > q.afterLast { + return Range[T]{} + } + return q +} + +// Iter iterates all integers in the range. +func (r Range[T]) Iter() iter.Seq[T] { + return func(yield func(T) bool) { + for i := r.first; i < r.afterLast; i++ { + if !yield(i) { + break + } + } + } +} diff --git a/common/range_test.go b/common/range_test.go new file mode 100644 index 0000000000..656b104b6b --- /dev/null +++ b/common/range_test.go @@ -0,0 +1,42 @@ +// Copyright 2025 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package common + +import ( + "slices" + "testing" +) + +func TestRangeIter(t *testing.T) { + var values []uint32 + r := NewRange[uint32](1, 7) + for i := range r.Iter() { + values = append(values, i) + } + if !slices.Equal(values, []uint32{1, 2, 3, 4, 5, 6, 7}) { + t.Fatalf("wrong iter values: %v", values) + } + + values = nil + empty := NewRange[uint32](1, 0) + for i := range empty.Iter() { + values = append(values, i) + } + if !slices.Equal(values, []uint32{}) { + t.Fatalf("wrong iter values: %v", values) + } +} diff --git a/common/types.go b/common/types.go index 89a559ef19..fdb25f1b34 100644 --- a/common/types.go +++ b/common/types.go @@ -23,7 +23,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "math/big" "math/rand" "reflect" @@ -31,7 +30,6 @@ import ( "strings" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/rlp" "golang.org/x/crypto/sha3" ) @@ -488,83 +486,3 @@ func (b PrettyBytes) TerminalString() string { } return fmt.Sprintf("%#x...%x (%dB)", b[:3], b[len(b)-3:], len(b)) } - -type Range[T uint32 | uint64] struct { - first, afterLast T -} - -func NewRange[T uint32 | uint64](first, count T) Range[T] { - return Range[T]{first, first + count} -} - -func (r *Range[T]) EncodeRLP(w io.Writer) error { - if err := rlp.Encode(w, &r.first); err != nil { - return err - } - return rlp.Encode(w, &r.afterLast) -} - -func (r *Range[T]) DecodeRLP(s *rlp.Stream) error { - if err := s.Decode(&r.first); err != nil { - return err - } - return s.Decode(&r.afterLast) -} - -func (r Range[T]) First() T { - return r.first -} - -func (r Range[T]) Last() T { - if r.first == r.afterLast { - panic("last item of zero length range is not allowed") - } - return r.afterLast - 1 -} - -func (r Range[T]) AfterLast() T { - return r.afterLast -} - -func (r Range[T]) Count() T { - return r.afterLast - r.first -} - -func (r Range[T]) IsEmpty() bool { - return r.first == r.afterLast -} - -func (r Range[T]) Includes(v T) bool { - return v >= r.first && v < r.afterLast -} - -func (r *Range[T]) SetFirst(v T) { - r.first = v - if r.afterLast < r.first { - r.afterLast = r.first - } -} - -func (r *Range[T]) SetAfterLast(v T) { - r.afterLast = v - if r.afterLast < r.first { - r.first = r.afterLast - } -} - -func (r *Range[T]) SetLast(v T) { - r.SetAfterLast(v + 1) -} - -func (r Range[T]) Intersection(q Range[T]) Range[T] { - if r.first > q.first { - q.first = r.first - } - if r.afterLast < q.afterLast { - q.afterLast = r.afterLast - } - if q.first > q.afterLast { - return Range[T]{} - } - return q -}