Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

planner,util/ranger: reduce mem allocs on rebuildRange #58362

Closed
wants to merge 2 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
planner,util/ranger: reduce mem allocs on rebuildRange
Signed-off-by: zyguan <[email protected]>
zyguan committed Dec 18, 2024
commit 8ac35f2057d68b9469cdd648530d9d246f0f5d91
20 changes: 20 additions & 0 deletions pkg/kv/key.go
Original file line number Diff line number Diff line change
@@ -67,6 +67,26 @@ func (k Key) PrefixNext() Key {
return buf
}

// PrefixNextInPlace tries to modifies the key itself to the next prefix key, returns false when lack of space (in which
// case the key is kept unchanged and the caller can append 0 to the key).
func (k Key) PrefixNextInPlace() bool {
var i int
for i = len(k) - 1; i >= 0; i-- {
k[i]++
if k[i] != 0 {
break
}
}
if i == -1 {
// revert the key
for i := range k {
k[i]--
}
return false
}
return true
}

// Cmp returns the comparison result of two key.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
func (k Key) Cmp(another Key) int {
27 changes: 20 additions & 7 deletions pkg/planner/core/plan_cache_rebuild.go
Original file line number Diff line number Diff line change
@@ -182,6 +182,7 @@ func convertConstant2Datum(ctx base.PlanContext, con *expression.Constant, targe
}

func buildRangeForTableScan(sctx base.PlanContext, ts *PhysicalTableScan) (err error) {
allocator := tryGetRangerAllocator(sctx)
if ts.Table.IsCommonHandle {
pk := tables.FindPrimaryIndex(ts.Table)
pkCols := make([]*expression.Column, 0, len(pk.Columns))
@@ -202,7 +203,7 @@ func buildRangeForTableScan(sctx base.PlanContext, ts *PhysicalTableScan) (err e
}
}
if len(pkCols) > 0 {
res, err := ranger.DetachCondAndBuildRangeForIndex(sctx.GetRangerCtx(), ts.AccessCondition, pkCols, pkColsLen, 0)
res, err := ranger.DetachCondAndBuildRangeForIndexWithAllocator(sctx.GetRangerCtx(), allocator, ts.AccessCondition, pkCols, pkColsLen, 0)
if err != nil {
return err
}
@@ -224,7 +225,7 @@ func buildRangeForTableScan(sctx base.PlanContext, ts *PhysicalTableScan) (err e
}
}
if pkCol != nil {
ranges, accessConds, remainingConds, err := ranger.BuildTableRange(ts.AccessCondition, sctx.GetRangerCtx(), pkCol.RetType, 0)
ranges, accessConds, remainingConds, err := ranger.BuildTableRangeWithAllocator(ts.AccessCondition, sctx.GetRangerCtx(), allocator, pkCol.RetType, 0)
if err != nil {
return err
}
@@ -254,8 +255,9 @@ func buildRangesForPointGet(sctx base.PlanContext, x *PointGetPlan) (err error)
}
// if access condition is not nil, which means it's a point get generated by cbo.
if x.AccessConditions != nil {
allocator := tryGetRangerAllocator(sctx)
if x.IndexInfo != nil {
ranges, err := ranger.DetachCondAndBuildRangeForIndex(x.SCtx().GetRangerCtx(), x.AccessConditions, x.IdxCols, x.IdxColLens, 0)
ranges, err := ranger.DetachCondAndBuildRangeForIndexWithAllocator(x.SCtx().GetRangerCtx(), allocator, x.AccessConditions, x.IdxCols, x.IdxColLens, 0)
if err != nil {
return err
}
@@ -277,7 +279,7 @@ func buildRangesForPointGet(sctx base.PlanContext, x *PointGetPlan) (err error)
}
}
if pkCol != nil {
ranges, accessConds, remainingConds, err := ranger.BuildTableRange(x.AccessConditions, x.SCtx().GetRangerCtx(), pkCol.RetType, 0)
ranges, accessConds, remainingConds, err := ranger.BuildTableRangeWithAllocator(x.AccessConditions, x.SCtx().GetRangerCtx(), allocator, pkCol.RetType, 0)
if err != nil {
return err
}
@@ -322,8 +324,9 @@ func buildRangesForBatchGet(sctx base.PlanContext, x *BatchPointGetPlan) (err er
}
// if access condition is not nil, which means it's a point get generated by cbo.
if x.AccessConditions != nil {
allocator := tryGetRangerAllocator(sctx)
if x.IndexInfo != nil {
ranges, err := ranger.DetachCondAndBuildRangeForIndex(x.ctx.GetRangerCtx(), x.AccessConditions, x.IdxCols, x.IdxColLens, 0)
ranges, err := ranger.DetachCondAndBuildRangeForIndexWithAllocator(x.ctx.GetRangerCtx(), allocator, x.AccessConditions, x.IdxCols, x.IdxColLens, 0)
if err != nil {
return err
}
@@ -345,7 +348,7 @@ func buildRangesForBatchGet(sctx base.PlanContext, x *BatchPointGetPlan) (err er
}
}
if pkCol != nil {
ranges, accessConds, remainingConds, err := ranger.BuildTableRange(x.AccessConditions, x.ctx.GetRangerCtx(), pkCol.RetType, 0)
ranges, accessConds, remainingConds, err := ranger.BuildTableRangeWithAllocator(x.AccessConditions, x.ctx.GetRangerCtx(), allocator, pkCol.RetType, 0)
if err != nil {
return err
}
@@ -411,7 +414,8 @@ func buildRangeForIndexScan(sctx base.PlanContext, is *PhysicalIndexScan) (err e
return errors.New("unexpected range for PhysicalIndexScan")
}

res, err := ranger.DetachCondAndBuildRangeForIndex(sctx.GetRangerCtx(), is.AccessCondition, is.IdxCols, is.IdxColLens, 0)
allocator := tryGetRangerAllocator(sctx)
res, err := ranger.DetachCondAndBuildRangeForIndexWithAllocator(sctx.GetRangerCtx(), allocator, is.AccessCondition, is.IdxCols, is.IdxColLens, 0)
if err != nil {
return err
}
@@ -444,3 +448,12 @@ func isSafeRange(accessConds []expression.Expression, rebuiltResult *ranger.Deta

return true
}

func tryGetRangerAllocator(ctx base.PlanContext) *ranger.Allocator {
if p, ok := ctx.(ranger.AllocatorProvider); ok {
allocator := p.GetRangerAllocator()
allocator.Reset()
return allocator
}
return nil
}
1 change: 1 addition & 0 deletions pkg/session/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -109,6 +109,7 @@ go_library(
"//pkg/util/logutil/consistency",
"//pkg/util/memory",
"//pkg/util/parser",
"//pkg/util/ranger",
"//pkg/util/ranger/context",
"//pkg/util/redact",
"//pkg/util/sem",
8 changes: 8 additions & 0 deletions pkg/session/contextimpl.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ package session
import (
"github.com/pingcap/tidb/pkg/planner/planctx"
"github.com/pingcap/tidb/pkg/planner/plannersession"
"github.com/pingcap/tidb/pkg/util/ranger"
)

var _ planctx.PlanContext = &planContextImpl{}
@@ -27,6 +28,8 @@ var _ planctx.PlanContext = &planContextImpl{}
type planContextImpl struct {
*session
*plannersession.PlanCtxExtended

allocator ranger.Allocator
}

// NewPlanContextImpl creates a new PlanContextImpl.
@@ -36,3 +39,8 @@ func newPlanContextImpl(s *session) *planContextImpl {
PlanCtxExtended: plannersession.NewPlanCtxExtended(s),
}
}

// GetRangerAllocator implements the `ranger.AllocatorProvider` interface.
func (ctx *planContextImpl) GetRangerAllocator() *ranger.Allocator {
return &ctx.allocator
}
1 change: 1 addition & 0 deletions pkg/util/ranger/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ go_library(
"//pkg/util/dbterror/plannererrors",
"//pkg/util/hack",
"//pkg/util/ranger/context",
"//pkg/util/zeropool",
"@com_github_pingcap_errors//:errors",
],
)
4 changes: 3 additions & 1 deletion pkg/util/ranger/bench_test.go
Original file line number Diff line number Diff line change
@@ -130,8 +130,10 @@ WHERE

b.ResetTimer()
pctx := sctx.GetPlanCtx()
allocator := new(ranger.Allocator)
for range b.N {
_, err = ranger.DetachCondAndBuildRangeForIndex(pctx.GetRangerCtx(), conds, cols, lengths, 0)
allocator.Reset()
_, err = ranger.DetachCondAndBuildRangeForIndexWithAllocator(pctx.GetRangerCtx(), allocator, conds, cols, lengths, 0)
require.NoError(b, err)
}
b.StopTimer()
58 changes: 37 additions & 21 deletions pkg/util/ranger/detacher.go
Original file line number Diff line number Diff line change
@@ -303,7 +303,7 @@ func mergeTwoCNFRanges(sctx *rangerctx.RangerContext, cond expression.Expression
// item ranges.
// e.g, for input CNF expressions ((a,b) in ((1,1),(2,2))) and a > 1 and ((a,b,c) in (1,1,1),(2,2,2))
// ((a,b,c) in (1,1,1),(2,2,2)) would be extracted.
func extractBestCNFItemRanges(sctx *rangerctx.RangerContext, conds []expression.Expression, cols []*expression.Column,
func extractBestCNFItemRanges(sctx *rangerctx.RangerContext, allocator *Allocator, conds []expression.Expression, cols []*expression.Column,
lengths []int, rangeMaxSize int64, convertToSortKey bool) (*cnfItemRangeResult, []*valueInfo, error) {
if len(conds) < 2 {
return nil, nil, nil
@@ -323,7 +323,7 @@ func extractBestCNFItemRanges(sctx *rangerctx.RangerContext, conds []expression.
// We build ranges for `(a,b) in ((1,1),(1,2))` and get `[1 1, 1 1] [1 2, 1 2]`, which are point ranges and we can
// append `c = 1` to the point ranges. However, if we choose to merge consecutive ranges here, we get `[1 1, 1 2]`,
// which are not point ranges, and we cannot append `c = 1` anymore.
res, err := detachCondAndBuildRange(sctx, tmpConds, cols, lengths, rangeMaxSize, convertToSortKey, false)
res, err := detachCondAndBuildRange(sctx, allocator, tmpConds, cols, lengths, rangeMaxSize, convertToSortKey, false)
if err != nil {
return nil, nil, err
}
@@ -389,15 +389,15 @@ func chooseBetweenRangeAndPoint(sctx *rangerctx.RangerContext, r1 *DetachRangeRe
// detachCNFCondAndBuildRangeForIndex will detach the index filters from table filters. These conditions are connected with `and`
// It will first find the point query column and then extract the range query column.
// considerDNF is true means it will try to extract access conditions from the DNF expressions.
func (d *rangeDetacher) detachCNFCondAndBuildRangeForIndex(conditions []expression.Expression, newTpSlice []*types.FieldType, considerDNF bool) (*DetachRangeResult, error) {
func (d *rangeDetacher) detachCNFCondAndBuildRangeForIndex(conditions []expression.Expression, newTpSlice []types.FieldType, considerDNF bool) (*DetachRangeResult, error) {
var (
eqCount int
ranges Ranges
err error
)
res := &DetachRangeResult{}

accessConds, filterConds, newConditions, columnValues, emptyRange := ExtractEqAndInCondition(d.sctx, conditions, d.cols, d.lengths)
accessConds, filterConds, newConditions, columnValues, emptyRange := extractEqAndInCondition(d.sctx, d.allocator, conditions, d.cols, d.lengths)
if emptyRange {
return res, nil
}
@@ -462,7 +462,7 @@ func (d *rangeDetacher) detachCNFCondAndBuildRangeForIndex(conditions []expressi
ctx: d.sctx.ExprCtx.GetEvalCtx(),
}
if considerDNF {
bestCNFItemRes, columnValues, err := extractBestCNFItemRanges(d.sctx, conditions, d.cols, d.lengths, d.rangeMaxSize, d.convertToSortKey)
bestCNFItemRes, columnValues, err := extractBestCNFItemRanges(d.sctx, d.allocator, conditions, d.cols, d.lengths, d.rangeMaxSize, d.convertToSortKey)
if err != nil {
return nil, err
}
@@ -503,7 +503,7 @@ func (d *rangeDetacher) detachCNFCondAndBuildRangeForIndex(conditions []expressi
if eqOrInCount > 0 {
newCols := d.cols[eqOrInCount:]
newLengths := d.lengths[eqOrInCount:]
tailRes, err := detachCondAndBuildRange(d.sctx, newConditions, newCols, newLengths, d.rangeMaxSize, d.convertToSortKey, d.mergeConsecutive)
tailRes, err := detachCondAndBuildRange(d.sctx, d.allocator, newConditions, newCols, newLengths, d.rangeMaxSize, d.convertToSortKey, d.mergeConsecutive)
if err != nil {
return nil, err
}
@@ -722,15 +722,22 @@ func extractValueInfo(expr expression.Expression) *valueInfo {
// columnValues: the constant column values for all index columns. columnValues[i] is nil if cols[i] is not constant.
// bool: indicate whether there's nil range when merging eq and in conditions.
func ExtractEqAndInCondition(sctx *rangerctx.RangerContext, conditions []expression.Expression, cols []*expression.Column,
lengths []int) ([]expression.Expression, []expression.Expression, []expression.Expression, []*valueInfo, bool) {
return extractEqAndInCondition(sctx, nil, conditions, cols, lengths)
}

// extractEqAndInCondition like ExtractEqAndInCondition, but it uses the given allocator.
func extractEqAndInCondition(sctx *rangerctx.RangerContext, allocator *Allocator, conditions []expression.Expression, cols []*expression.Column,
lengths []int) ([]expression.Expression, []expression.Expression, []expression.Expression, []*valueInfo, bool) {
var filters []expression.Expression
rb := builder{sctx: sctx}
rb := builder{sctx: sctx, allocator: allocator}
accesses := make([]expression.Expression, len(cols))
points := make([][]*point, len(cols))
mergedAccesses := make([]expression.Expression, len(cols))
newConditions := make([]expression.Expression, 0, len(conditions))
columnValues := make([]*valueInfo, len(cols))
offsets := make([]int, len(conditions))
var newTpVal types.FieldType
for i, cond := range conditions {
offset := getPotentialEqOrInColOffset(sctx, cond, cols)
offsets[i] = offset
@@ -743,16 +750,16 @@ func ExtractEqAndInCondition(sctx *rangerctx.RangerContext, conditions []express
}
// Multiple Eq/In conditions for one column in CNF, apply intersection on them
// Lazily compute the points for the previously visited Eq/In
newTp := newFieldType(cols[offset].GetType(sctx.ExprCtx.GetEvalCtx()))
newTpVal = newFieldTypeValue(cols[offset].GetType(sctx.ExprCtx.GetEvalCtx()))
collator := collate.GetCollator(cols[offset].GetType(sctx.ExprCtx.GetEvalCtx()).GetCollate())
if mergedAccesses[offset] == nil {
mergedAccesses[offset] = accesses[offset]
// Note that this is a relatively special usage of build(). We will restore the points back to Expression for
// later use and may build the Expression to points again.
// We need to keep the original value here, which means we neither cut prefix nor convert to sort key.
points[offset] = rb.build(accesses[offset], newTp, types.UnspecifiedLength, false)
points[offset] = rb.build(accesses[offset], &newTpVal, types.UnspecifiedLength, false)
}
points[offset] = rb.intersection(points[offset], rb.build(cond, newTp, types.UnspecifiedLength, false), collator)
points[offset] = rb.intersection(points[offset], rb.build(cond, &newTpVal, types.UnspecifiedLength, false), collator)
if len(points[offset]) == 0 { // Early termination if false expression found
if expression.MaybeOverOptimized4PlanCache(sctx.ExprCtx, conditions) {
// `a>@x and a<@y` --> `invalid-range if @x>=@y`
@@ -835,7 +842,7 @@ func ExtractEqAndInCondition(sctx *rangerctx.RangerContext, conditions []express
// We will detach the conditions of every DNF items, then compose them to a DNF.
func (d *rangeDetacher) detachDNFCondAndBuildRangeForIndex(
condition *expression.ScalarFunction,
newTpSlice []*types.FieldType,
newTpSlice []types.FieldType,
) (
Ranges,
[]expression.Expression,
@@ -850,7 +857,7 @@ func (d *rangeDetacher) detachDNFCondAndBuildRangeForIndex(
optPrefixIndexSingleScan: d.sctx.OptPrefixIndexSingleScan,
ctx: d.sctx.ExprCtx.GetEvalCtx(),
}
rb := builder{sctx: d.sctx}
rb := builder{sctx: d.sctx, allocator: d.allocator}
dnfItems := expression.FlattenDNFConditions(condition)
newAccessItems := make([]expression.Expression, 0, len(dnfItems))
minAccessConds := -1
@@ -918,13 +925,13 @@ func (d *rangeDetacher) detachDNFCondAndBuildRangeForIndex(
if shouldReserve {
hasResidual = true
}
points := rb.build(item, newTpSlice[0], d.lengths[0], d.convertToSortKey)
tmpNewTp := newTpSlice[0]
points := rb.build(item, &newTpSlice[0], d.lengths[0], d.convertToSortKey)
tmpNewTp := &newTpSlice[0]
if d.convertToSortKey {
tmpNewTp = convertStringFTToBinaryCollate(tmpNewTp)
}
// TODO: restrict the mem usage of ranges
ranges, rangeFallback, err := points2Ranges(d.sctx, points, tmpNewTp, d.rangeMaxSize)
ranges, rangeFallback, err := points2Ranges(&rb, points, tmpNewTp, d.rangeMaxSize)
if err != nil {
return nil, nil, nil, false, -1, errors.Trace(err)
}
@@ -1020,8 +1027,15 @@ type DetachRangeResult struct {
// The returned values are encapsulated into a struct DetachRangeResult, see its comments for explanation.
func DetachCondAndBuildRangeForIndex(sctx *rangerctx.RangerContext, conditions []expression.Expression, cols []*expression.Column,
lengths []int, rangeMaxSize int64) (*DetachRangeResult, error) {
return DetachCondAndBuildRangeForIndexWithAllocator(sctx, nil, conditions, cols, lengths, rangeMaxSize)
}

// DetachCondAndBuildRangeForIndexWithAllocator likes DetachCondAndBuildRangeForIndex, but it uses the given allocator.
func DetachCondAndBuildRangeForIndexWithAllocator(sctx *rangerctx.RangerContext, allocator *Allocator, conditions []expression.Expression,
cols []*expression.Column, lengths []int, rangeMaxSize int64) (*DetachRangeResult, error) {
d := &rangeDetacher{
sctx: sctx,
allocator: allocator,
allConds: conditions,
cols: cols,
lengths: lengths,
@@ -1033,10 +1047,11 @@ func DetachCondAndBuildRangeForIndex(sctx *rangerctx.RangerContext, conditions [
}

// detachCondAndBuildRange detaches the index filters from table filters and uses them to build ranges.
func detachCondAndBuildRange(sctx *rangerctx.RangerContext, conditions []expression.Expression, cols []*expression.Column,
func detachCondAndBuildRange(sctx *rangerctx.RangerContext, allocator *Allocator, conditions []expression.Expression, cols []*expression.Column,
lengths []int, rangeMaxSize int64, convertToSortKey bool, mergeConsecutive bool) (*DetachRangeResult, error) {
d := &rangeDetacher{
sctx: sctx,
allocator: allocator,
allConds: conditions,
cols: cols,
lengths: lengths,
@@ -1053,11 +1068,12 @@ func detachCondAndBuildRange(sctx *rangerctx.RangerContext, conditions []express
// The returned values are encapsulated into a struct DetachRangeResult, see its comments for explanation.
func DetachCondAndBuildRangeForPartition(sctx *rangerctx.RangerContext, conditions []expression.Expression, cols []*expression.Column,
lengths []int, rangeMaxSize int64) (*DetachRangeResult, error) {
return detachCondAndBuildRange(sctx, conditions, cols, lengths, rangeMaxSize, false, false)
return detachCondAndBuildRange(sctx, nil, conditions, cols, lengths, rangeMaxSize, false, false)
}

type rangeDetacher struct {
sctx *rangerctx.RangerContext
allocator *Allocator
allConds []expression.Expression
cols []*expression.Column
lengths []int
@@ -1068,9 +1084,9 @@ type rangeDetacher struct {

func (d *rangeDetacher) detachCondAndBuildRangeForCols() (*DetachRangeResult, error) {
res := &DetachRangeResult{}
newTpSlice := make([]*types.FieldType, 0, len(d.cols))
newTpSlice := make([]types.FieldType, 0, len(d.cols))
for _, col := range d.cols {
newTpSlice = append(newTpSlice, newFieldType(col.RetType))
newTpSlice = append(newTpSlice, newFieldTypeValue(col.RetType))
}
if len(d.allConds) == 1 {
if sf, ok := d.allConds[0].(*expression.ScalarFunction); ok && sf.FuncName.L == ast.LogicOr {
@@ -1103,9 +1119,9 @@ func (d *rangeDetacher) detachCondAndBuildRangeForCols() (*DetachRangeResult, er
// for building ranges, set rangeMemQuota to 0 to avoid range fallback.
func DetachSimpleCondAndBuildRangeForIndex(sctx *rangerctx.RangerContext, conditions []expression.Expression,
cols []*expression.Column, lengths []int, rangeMaxSize int64) (Ranges, []expression.Expression, error) {
newTpSlice := make([]*types.FieldType, 0, len(cols))
newTpSlice := make([]types.FieldType, 0, len(cols))
for _, col := range cols {
newTpSlice = append(newTpSlice, newFieldType(col.RetType))
newTpSlice = append(newTpSlice, newFieldTypeValue(col.RetType))
}
d := &rangeDetacher{
sctx: sctx,
252 changes: 124 additions & 128 deletions pkg/util/ranger/points.go

Large diffs are not rendered by default.

174 changes: 86 additions & 88 deletions pkg/util/ranger/ranger.go

Large diffs are not rendered by default.

102 changes: 71 additions & 31 deletions pkg/util/ranger/ranger_test.go

Large diffs are not rendered by default.

102 changes: 98 additions & 4 deletions pkg/util/ranger/types.go
Original file line number Diff line number Diff line change
@@ -502,7 +502,7 @@ func (ran *Range) IntersectRange(tc types.Context, otherRange *Range) (*Range, e
lowVsHigh, err := compareLexicographically(tc, ran.LowVal, otherRange.HighVal, ran.Collators,
ran.LowExclude, otherRange.HighExclude, true, false)
if err != nil {
return &Range{}, err
return nil, err
}
if lowVsHigh == 1 {
return nil, nil
@@ -511,7 +511,7 @@ func (ran *Range) IntersectRange(tc types.Context, otherRange *Range) (*Range, e
lowVsHigh, err = compareLexicographically(tc, otherRange.LowVal, ran.HighVal, ran.Collators,
otherRange.LowExclude, ran.HighExclude, true, false)
if err != nil {
return &Range{}, err
return nil, err
}
if lowVsHigh == 1 {
return nil, nil
@@ -520,7 +520,7 @@ func (ran *Range) IntersectRange(tc types.Context, otherRange *Range) (*Range, e
lowVsLow, err := compareLexicographically(tc, ran.LowVal, otherRange.LowVal,
ran.Collators, ran.LowExclude, otherRange.LowExclude, true, true)
if err != nil {
return &Range{}, err
return nil, err
}
if lowVsLow == -1 {
result.LowVal = otherRange.LowVal
@@ -533,7 +533,7 @@ func (ran *Range) IntersectRange(tc types.Context, otherRange *Range) (*Range, e
highVsHigh, err := compareLexicographically(tc, ran.HighVal, otherRange.HighVal,
ran.Collators, ran.HighExclude, otherRange.HighExclude, false, false)
if err != nil {
return &Range{}, err
return nil, err
}
if highVsHigh == 1 {
result.HighVal = otherRange.HighVal
@@ -565,3 +565,97 @@ func (rs Ranges) IntersectRanges(tc types.Context, otherRanges Ranges) Ranges {
}
return result
}

// AllocatorProvider defines the interface for getting an Allocator.
type AllocatorProvider interface {
GetRangerAllocator() *Allocator
}

type baseRangeData struct {
lval [1]types.Datum
hval [1]types.Datum
collator [1]collate.Collator
Range
}

// Allocator is used to allocate points and ranges. It reuses the underlying slices to reduce allocations.
type Allocator struct {
points []point
slices [][4]*point
ranges []baseRangeData
sindex int
rindex int
}

// Reset resets the underlying data slices for reusing.
func (a *Allocator) Reset() {
a.points = a.points[:0]
a.sindex = 0
a.rindex = 0
}

func (a *Allocator) newPoint(value types.Datum, excl bool, start bool) *point {
if a == nil {
return &point{value: value, excl: excl, start: start}
}
a.points = append(a.points, point{value: value, excl: excl, start: start})
return &a.points[len(a.points)-1]
}

func (a *Allocator) points2(p1, p2 *point) []*point {
if a == nil {
return []*point{p1, p2}
}
if a.sindex >= len(a.slices) {
a.slices = append(a.slices, [4]*point{})
}
points := append(a.slices[a.sindex][:0], p1, p2)
a.sindex++
return points
}

func (a *Allocator) points4(p1, p2, p3, p4 *point) []*point {
if a == nil {
return []*point{p1, p2, p3, p4}
}
if a.sindex >= len(a.slices) {
a.slices = append(a.slices, [4]*point{})
}
points := append(a.slices[a.sindex][:0], p1, p2, p3, p4)
a.sindex++
return points
}

func (a *Allocator) newRange(p1, p2 *point, collator collate.Collator) *Range {
if a == nil {
return &Range{
LowVal: []types.Datum{p1.value},
LowExclude: p1.excl,
HighVal: []types.Datum{p2.value},
HighExclude: p2.excl,
Collators: []collate.Collator{collator},
}
}
if a.rindex >= len(a.ranges) {
a.ranges = append(a.ranges, baseRangeData{})
}
data := &a.ranges[a.rindex]
data.lval[0] = p1.value
data.Range.LowVal = data.lval[:]
data.Range.LowExclude = p1.excl
data.hval[0] = p2.value
data.Range.HighVal = data.hval[:]
data.Range.HighExclude = p2.excl
data.collator[0] = collator
data.Range.Collators = data.collator[:]
a.rindex++
return &data.Range
}

func (a *Allocator) rangesFromPoints(points []*point, collator collate.Collator) Ranges {
ranges := make(Ranges, 0, len(points)/2)
for i := 0; i < len(points); i += 2 {
ranges = append(ranges, a.newRange(points[i], points[i+1], collator))
}
return ranges
}