mirror of
https://git.mirrors.martin98.com/https://github.com/ceph/ceph-csi.git
synced 2025-08-14 07:35:56 +08:00

Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
728 lines
21 KiB
Go
728 lines
21 KiB
Go
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
|
// Use of this file is governed by the BSD 3-clause license that
|
|
// can be found in the LICENSE.txt file in the project root.
|
|
|
|
package antlr
|
|
|
|
import (
|
|
"fmt"
|
|
"golang.org/x/exp/slices"
|
|
"strconv"
|
|
)
|
|
|
|
var _emptyPredictionContextHash int
|
|
|
|
func init() {
|
|
_emptyPredictionContextHash = murmurInit(1)
|
|
_emptyPredictionContextHash = murmurFinish(_emptyPredictionContextHash, 0)
|
|
}
|
|
|
|
func calculateEmptyHash() int {
|
|
return _emptyPredictionContextHash
|
|
}
|
|
|
|
const (
|
|
// BasePredictionContextEmptyReturnState represents {@code $} in an array in full context mode, $
|
|
// doesn't mean wildcard:
|
|
//
|
|
// $ + x = [$,x]
|
|
//
|
|
// Here,
|
|
//
|
|
// $ = EmptyReturnState
|
|
BasePredictionContextEmptyReturnState = 0x7FFFFFFF
|
|
)
|
|
|
|
// TODO: JI These are meant to be atomics - this does not seem to match the Java runtime here
|
|
//
|
|
//goland:noinspection GoUnusedGlobalVariable
|
|
var (
|
|
BasePredictionContextglobalNodeCount = 1
|
|
BasePredictionContextid = BasePredictionContextglobalNodeCount
|
|
)
|
|
|
|
const (
|
|
PredictionContextEmpty = iota
|
|
PredictionContextSingleton
|
|
PredictionContextArray
|
|
)
|
|
|
|
// PredictionContext is a go idiomatic implementation of PredictionContext that does not rty to
|
|
// emulate inheritance from Java, and can be used without an interface definition. An interface
|
|
// is not required because no user code will ever need to implement this interface.
|
|
type PredictionContext struct {
|
|
cachedHash int
|
|
pcType int
|
|
parentCtx *PredictionContext
|
|
returnState int
|
|
parents []*PredictionContext
|
|
returnStates []int
|
|
}
|
|
|
|
func NewEmptyPredictionContext() *PredictionContext {
|
|
nep := &PredictionContext{}
|
|
nep.cachedHash = calculateEmptyHash()
|
|
nep.pcType = PredictionContextEmpty
|
|
nep.returnState = BasePredictionContextEmptyReturnState
|
|
return nep
|
|
}
|
|
|
|
func NewBaseSingletonPredictionContext(parent *PredictionContext, returnState int) *PredictionContext {
|
|
pc := &PredictionContext{}
|
|
pc.pcType = PredictionContextSingleton
|
|
pc.returnState = returnState
|
|
pc.parentCtx = parent
|
|
if parent != nil {
|
|
pc.cachedHash = calculateHash(parent, returnState)
|
|
} else {
|
|
pc.cachedHash = calculateEmptyHash()
|
|
}
|
|
return pc
|
|
}
|
|
|
|
func SingletonBasePredictionContextCreate(parent *PredictionContext, returnState int) *PredictionContext {
|
|
if returnState == BasePredictionContextEmptyReturnState && parent == nil {
|
|
// someone can pass in the bits of an array ctx that mean $
|
|
return BasePredictionContextEMPTY
|
|
}
|
|
return NewBaseSingletonPredictionContext(parent, returnState)
|
|
}
|
|
|
|
func NewArrayPredictionContext(parents []*PredictionContext, returnStates []int) *PredictionContext {
|
|
// Parent can be nil only if full ctx mode and we make an array
|
|
// from {@link //EMPTY} and non-empty. We merge {@link //EMPTY} by using
|
|
// nil parent and
|
|
// returnState == {@link //EmptyReturnState}.
|
|
hash := murmurInit(1)
|
|
for _, parent := range parents {
|
|
hash = murmurUpdate(hash, parent.Hash())
|
|
}
|
|
for _, returnState := range returnStates {
|
|
hash = murmurUpdate(hash, returnState)
|
|
}
|
|
hash = murmurFinish(hash, len(parents)<<1)
|
|
|
|
nec := &PredictionContext{}
|
|
nec.cachedHash = hash
|
|
nec.pcType = PredictionContextArray
|
|
nec.parents = parents
|
|
nec.returnStates = returnStates
|
|
return nec
|
|
}
|
|
|
|
func (p *PredictionContext) Hash() int {
|
|
return p.cachedHash
|
|
}
|
|
|
|
func (p *PredictionContext) Equals(other Collectable[*PredictionContext]) bool {
|
|
switch p.pcType {
|
|
case PredictionContextEmpty:
|
|
otherP := other.(*PredictionContext)
|
|
return other == nil || otherP == nil || otherP.isEmpty()
|
|
case PredictionContextSingleton:
|
|
return p.SingletonEquals(other)
|
|
case PredictionContextArray:
|
|
return p.ArrayEquals(other)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (p *PredictionContext) ArrayEquals(o Collectable[*PredictionContext]) bool {
|
|
if o == nil {
|
|
return false
|
|
}
|
|
other := o.(*PredictionContext)
|
|
if other == nil || other.pcType != PredictionContextArray {
|
|
return false
|
|
}
|
|
if p.cachedHash != other.Hash() {
|
|
return false // can't be same if hash is different
|
|
}
|
|
|
|
// Must compare the actual array elements and not just the array address
|
|
//
|
|
return slices.Equal(p.returnStates, other.returnStates) &&
|
|
slices.EqualFunc(p.parents, other.parents, func(x, y *PredictionContext) bool {
|
|
return x.Equals(y)
|
|
})
|
|
}
|
|
|
|
func (p *PredictionContext) SingletonEquals(other Collectable[*PredictionContext]) bool {
|
|
if other == nil {
|
|
return false
|
|
}
|
|
otherP := other.(*PredictionContext)
|
|
if otherP == nil {
|
|
return false
|
|
}
|
|
|
|
if p.cachedHash != otherP.Hash() {
|
|
return false // Can't be same if hash is different
|
|
}
|
|
|
|
if p.returnState != otherP.getReturnState(0) {
|
|
return false
|
|
}
|
|
|
|
// Both parents must be nil if one is
|
|
if p.parentCtx == nil {
|
|
return otherP.parentCtx == nil
|
|
}
|
|
|
|
return p.parentCtx.Equals(otherP.parentCtx)
|
|
}
|
|
|
|
func (p *PredictionContext) GetParent(i int) *PredictionContext {
|
|
switch p.pcType {
|
|
case PredictionContextEmpty:
|
|
return nil
|
|
case PredictionContextSingleton:
|
|
return p.parentCtx
|
|
case PredictionContextArray:
|
|
return p.parents[i]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *PredictionContext) getReturnState(i int) int {
|
|
switch p.pcType {
|
|
case PredictionContextArray:
|
|
return p.returnStates[i]
|
|
default:
|
|
return p.returnState
|
|
}
|
|
}
|
|
|
|
func (p *PredictionContext) GetReturnStates() []int {
|
|
switch p.pcType {
|
|
case PredictionContextArray:
|
|
return p.returnStates
|
|
default:
|
|
return []int{p.returnState}
|
|
}
|
|
}
|
|
|
|
func (p *PredictionContext) length() int {
|
|
switch p.pcType {
|
|
case PredictionContextArray:
|
|
return len(p.returnStates)
|
|
default:
|
|
return 1
|
|
}
|
|
}
|
|
|
|
func (p *PredictionContext) hasEmptyPath() bool {
|
|
switch p.pcType {
|
|
case PredictionContextSingleton:
|
|
return p.returnState == BasePredictionContextEmptyReturnState
|
|
}
|
|
return p.getReturnState(p.length()-1) == BasePredictionContextEmptyReturnState
|
|
}
|
|
|
|
func (p *PredictionContext) String() string {
|
|
switch p.pcType {
|
|
case PredictionContextEmpty:
|
|
return "$"
|
|
case PredictionContextSingleton:
|
|
var up string
|
|
|
|
if p.parentCtx == nil {
|
|
up = ""
|
|
} else {
|
|
up = p.parentCtx.String()
|
|
}
|
|
|
|
if len(up) == 0 {
|
|
if p.returnState == BasePredictionContextEmptyReturnState {
|
|
return "$"
|
|
}
|
|
|
|
return strconv.Itoa(p.returnState)
|
|
}
|
|
|
|
return strconv.Itoa(p.returnState) + " " + up
|
|
case PredictionContextArray:
|
|
if p.isEmpty() {
|
|
return "[]"
|
|
}
|
|
|
|
s := "["
|
|
for i := 0; i < len(p.returnStates); i++ {
|
|
if i > 0 {
|
|
s = s + ", "
|
|
}
|
|
if p.returnStates[i] == BasePredictionContextEmptyReturnState {
|
|
s = s + "$"
|
|
continue
|
|
}
|
|
s = s + strconv.Itoa(p.returnStates[i])
|
|
if !p.parents[i].isEmpty() {
|
|
s = s + " " + p.parents[i].String()
|
|
} else {
|
|
s = s + "nil"
|
|
}
|
|
}
|
|
return s + "]"
|
|
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
func (p *PredictionContext) isEmpty() bool {
|
|
switch p.pcType {
|
|
case PredictionContextEmpty:
|
|
return true
|
|
case PredictionContextArray:
|
|
// since EmptyReturnState can only appear in the last position, we
|
|
// don't need to verify that size==1
|
|
return p.returnStates[0] == BasePredictionContextEmptyReturnState
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (p *PredictionContext) Type() int {
|
|
return p.pcType
|
|
}
|
|
|
|
func calculateHash(parent *PredictionContext, returnState int) int {
|
|
h := murmurInit(1)
|
|
h = murmurUpdate(h, parent.Hash())
|
|
h = murmurUpdate(h, returnState)
|
|
return murmurFinish(h, 2)
|
|
}
|
|
|
|
// Convert a {@link RuleContext} tree to a {@link BasePredictionContext} graph.
|
|
// Return {@link //EMPTY} if {@code outerContext} is empty or nil.
|
|
// /
|
|
func predictionContextFromRuleContext(a *ATN, outerContext RuleContext) *PredictionContext {
|
|
if outerContext == nil {
|
|
outerContext = ParserRuleContextEmpty
|
|
}
|
|
// if we are in RuleContext of start rule, s, then BasePredictionContext
|
|
// is EMPTY. Nobody called us. (if we are empty, return empty)
|
|
if outerContext.GetParent() == nil || outerContext == ParserRuleContextEmpty {
|
|
return BasePredictionContextEMPTY
|
|
}
|
|
// If we have a parent, convert it to a BasePredictionContext graph
|
|
parent := predictionContextFromRuleContext(a, outerContext.GetParent().(RuleContext))
|
|
state := a.states[outerContext.GetInvokingState()]
|
|
transition := state.GetTransitions()[0]
|
|
|
|
return SingletonBasePredictionContextCreate(parent, transition.(*RuleTransition).followState.GetStateNumber())
|
|
}
|
|
|
|
func merge(a, b *PredictionContext, rootIsWildcard bool, mergeCache *JPCMap) *PredictionContext {
|
|
|
|
// Share same graph if both same
|
|
//
|
|
if a == b || a.Equals(b) {
|
|
return a
|
|
}
|
|
|
|
if a.pcType == PredictionContextSingleton && b.pcType == PredictionContextSingleton {
|
|
return mergeSingletons(a, b, rootIsWildcard, mergeCache)
|
|
}
|
|
// At least one of a or b is array
|
|
// If one is $ and rootIsWildcard, return $ as wildcard
|
|
if rootIsWildcard {
|
|
if a.isEmpty() {
|
|
return a
|
|
}
|
|
if b.isEmpty() {
|
|
return b
|
|
}
|
|
}
|
|
|
|
// Convert either Singleton or Empty to arrays, so that we can merge them
|
|
//
|
|
ara := convertToArray(a)
|
|
arb := convertToArray(b)
|
|
return mergeArrays(ara, arb, rootIsWildcard, mergeCache)
|
|
}
|
|
|
|
func convertToArray(pc *PredictionContext) *PredictionContext {
|
|
switch pc.Type() {
|
|
case PredictionContextEmpty:
|
|
return NewArrayPredictionContext([]*PredictionContext{}, []int{})
|
|
case PredictionContextSingleton:
|
|
return NewArrayPredictionContext([]*PredictionContext{pc.GetParent(0)}, []int{pc.getReturnState(0)})
|
|
default:
|
|
// Already an array
|
|
}
|
|
return pc
|
|
}
|
|
|
|
// mergeSingletons merges two Singleton [PredictionContext] instances.
|
|
//
|
|
// Stack tops equal, parents merge is same return left graph.
|
|
// <embed src="images/SingletonMerge_SameRootSamePar.svg"
|
|
// type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Same stack top, parents differ merge parents giving array node, then
|
|
// remainders of those graphs. A new root node is created to point to the
|
|
// merged parents.<br>
|
|
// <embed src="images/SingletonMerge_SameRootDiffPar.svg"
|
|
// type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Different stack tops pointing to same parent. Make array node for the
|
|
// root where both element in the root point to the same (original)
|
|
// parent.<br>
|
|
// <embed src="images/SingletonMerge_DiffRootSamePar.svg"
|
|
// type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Different stack tops pointing to different parents. Make array node for
|
|
// the root where each element points to the corresponding original
|
|
// parent.<br>
|
|
// <embed src="images/SingletonMerge_DiffRootDiffPar.svg"
|
|
// type="image/svg+xml"/></p>
|
|
//
|
|
// @param a the first {@link SingletonBasePredictionContext}
|
|
// @param b the second {@link SingletonBasePredictionContext}
|
|
// @param rootIsWildcard {@code true} if this is a local-context merge,
|
|
// otherwise false to indicate a full-context merge
|
|
// @param mergeCache
|
|
// /
|
|
func mergeSingletons(a, b *PredictionContext, rootIsWildcard bool, mergeCache *JPCMap) *PredictionContext {
|
|
if mergeCache != nil {
|
|
previous, present := mergeCache.Get(a, b)
|
|
if present {
|
|
return previous
|
|
}
|
|
previous, present = mergeCache.Get(b, a)
|
|
if present {
|
|
return previous
|
|
}
|
|
}
|
|
|
|
rootMerge := mergeRoot(a, b, rootIsWildcard)
|
|
if rootMerge != nil {
|
|
if mergeCache != nil {
|
|
mergeCache.Put(a, b, rootMerge)
|
|
}
|
|
return rootMerge
|
|
}
|
|
if a.returnState == b.returnState {
|
|
parent := merge(a.parentCtx, b.parentCtx, rootIsWildcard, mergeCache)
|
|
// if parent is same as existing a or b parent or reduced to a parent,
|
|
// return it
|
|
if parent.Equals(a.parentCtx) {
|
|
return a // ax + bx = ax, if a=b
|
|
}
|
|
if parent.Equals(b.parentCtx) {
|
|
return b // ax + bx = bx, if a=b
|
|
}
|
|
// else: ax + ay = a'[x,y]
|
|
// merge parents x and y, giving array node with x,y then remainders
|
|
// of those graphs. dup a, a' points at merged array.
|
|
// New joined parent so create a new singleton pointing to it, a'
|
|
spc := SingletonBasePredictionContextCreate(parent, a.returnState)
|
|
if mergeCache != nil {
|
|
mergeCache.Put(a, b, spc)
|
|
}
|
|
return spc
|
|
}
|
|
// a != b payloads differ
|
|
// see if we can collapse parents due to $+x parents if local ctx
|
|
var singleParent *PredictionContext
|
|
if a.Equals(b) || (a.parentCtx != nil && a.parentCtx.Equals(b.parentCtx)) { // ax +
|
|
// bx =
|
|
// [a,b]x
|
|
singleParent = a.parentCtx
|
|
}
|
|
if singleParent != nil { // parents are same
|
|
// sort payloads and use same parent
|
|
payloads := []int{a.returnState, b.returnState}
|
|
if a.returnState > b.returnState {
|
|
payloads[0] = b.returnState
|
|
payloads[1] = a.returnState
|
|
}
|
|
parents := []*PredictionContext{singleParent, singleParent}
|
|
apc := NewArrayPredictionContext(parents, payloads)
|
|
if mergeCache != nil {
|
|
mergeCache.Put(a, b, apc)
|
|
}
|
|
return apc
|
|
}
|
|
// parents differ and can't merge them. Just pack together
|
|
// into array can't merge.
|
|
// ax + by = [ax,by]
|
|
payloads := []int{a.returnState, b.returnState}
|
|
parents := []*PredictionContext{a.parentCtx, b.parentCtx}
|
|
if a.returnState > b.returnState { // sort by payload
|
|
payloads[0] = b.returnState
|
|
payloads[1] = a.returnState
|
|
parents = []*PredictionContext{b.parentCtx, a.parentCtx}
|
|
}
|
|
apc := NewArrayPredictionContext(parents, payloads)
|
|
if mergeCache != nil {
|
|
mergeCache.Put(a, b, apc)
|
|
}
|
|
return apc
|
|
}
|
|
|
|
// Handle case where at least one of {@code a} or {@code b} is
|
|
// {@link //EMPTY}. In the following diagrams, the symbol {@code $} is used
|
|
// to represent {@link //EMPTY}.
|
|
//
|
|
// <h2>Local-Context Merges</h2>
|
|
//
|
|
// <p>These local-context merge operations are used when {@code rootIsWildcard}
|
|
// is true.</p>
|
|
//
|
|
// <p>{@link //EMPTY} is superset of any graph return {@link //EMPTY}.<br>
|
|
// <embed src="images/LocalMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// <p>{@link //EMPTY} and anything is {@code //EMPTY}, so merged parent is
|
|
// {@code //EMPTY} return left graph.<br>
|
|
// <embed src="images/LocalMerge_EmptyParent.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Special case of last merge if local context.<br>
|
|
// <embed src="images/LocalMerge_DiffRoots.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// <h2>Full-Context Merges</h2>
|
|
//
|
|
// <p>These full-context merge operations are used when {@code rootIsWildcard}
|
|
// is false.</p>
|
|
//
|
|
// <p><embed src="images/FullMerge_EmptyRoots.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Must keep all contexts {@link //EMPTY} in array is a special value (and
|
|
// nil parent).<br>
|
|
// <embed src="images/FullMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// <p><embed src="images/FullMerge_SameRoot.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// @param a the first {@link SingletonBasePredictionContext}
|
|
// @param b the second {@link SingletonBasePredictionContext}
|
|
// @param rootIsWildcard {@code true} if this is a local-context merge,
|
|
// otherwise false to indicate a full-context merge
|
|
// /
|
|
func mergeRoot(a, b *PredictionContext, rootIsWildcard bool) *PredictionContext {
|
|
if rootIsWildcard {
|
|
if a.pcType == PredictionContextEmpty {
|
|
return BasePredictionContextEMPTY // // + b =//
|
|
}
|
|
if b.pcType == PredictionContextEmpty {
|
|
return BasePredictionContextEMPTY // a +// =//
|
|
}
|
|
} else {
|
|
if a.isEmpty() && b.isEmpty() {
|
|
return BasePredictionContextEMPTY // $ + $ = $
|
|
} else if a.isEmpty() { // $ + x = [$,x]
|
|
payloads := []int{b.getReturnState(-1), BasePredictionContextEmptyReturnState}
|
|
parents := []*PredictionContext{b.GetParent(-1), nil}
|
|
return NewArrayPredictionContext(parents, payloads)
|
|
} else if b.isEmpty() { // x + $ = [$,x] ($ is always first if present)
|
|
payloads := []int{a.getReturnState(-1), BasePredictionContextEmptyReturnState}
|
|
parents := []*PredictionContext{a.GetParent(-1), nil}
|
|
return NewArrayPredictionContext(parents, payloads)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Merge two {@link ArrayBasePredictionContext} instances.
|
|
//
|
|
// <p>Different tops, different parents.<br>
|
|
// <embed src="images/ArrayMerge_DiffTopDiffPar.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Shared top, same parents.<br>
|
|
// <embed src="images/ArrayMerge_ShareTopSamePar.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Shared top, different parents.<br>
|
|
// <embed src="images/ArrayMerge_ShareTopDiffPar.svg" type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Shared top, all shared parents.<br>
|
|
// <embed src="images/ArrayMerge_ShareTopSharePar.svg"
|
|
// type="image/svg+xml"/></p>
|
|
//
|
|
// <p>Equal tops, merge parents and reduce top to
|
|
// {@link SingletonBasePredictionContext}.<br>
|
|
// <embed src="images/ArrayMerge_EqualTop.svg" type="image/svg+xml"/></p>
|
|
//
|
|
//goland:noinspection GoBoolExpressions
|
|
func mergeArrays(a, b *PredictionContext, rootIsWildcard bool, mergeCache *JPCMap) *PredictionContext {
|
|
if mergeCache != nil {
|
|
previous, present := mergeCache.Get(a, b)
|
|
if present {
|
|
if runtimeConfig.parserATNSimulatorTraceATNSim {
|
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> previous")
|
|
}
|
|
return previous
|
|
}
|
|
previous, present = mergeCache.Get(b, a)
|
|
if present {
|
|
if runtimeConfig.parserATNSimulatorTraceATNSim {
|
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> previous")
|
|
}
|
|
return previous
|
|
}
|
|
}
|
|
// merge sorted payloads a + b => M
|
|
i := 0 // walks a
|
|
j := 0 // walks b
|
|
k := 0 // walks target M array
|
|
|
|
mergedReturnStates := make([]int, len(a.returnStates)+len(b.returnStates))
|
|
mergedParents := make([]*PredictionContext, len(a.returnStates)+len(b.returnStates))
|
|
// walk and merge to yield mergedParents, mergedReturnStates
|
|
for i < len(a.returnStates) && j < len(b.returnStates) {
|
|
aParent := a.parents[i]
|
|
bParent := b.parents[j]
|
|
if a.returnStates[i] == b.returnStates[j] {
|
|
// same payload (stack tops are equal), must yield merged singleton
|
|
payload := a.returnStates[i]
|
|
// $+$ = $
|
|
bothDollars := payload == BasePredictionContextEmptyReturnState && aParent == nil && bParent == nil
|
|
axAX := aParent != nil && bParent != nil && aParent.Equals(bParent) // ax+ax
|
|
// ->
|
|
// ax
|
|
if bothDollars || axAX {
|
|
mergedParents[k] = aParent // choose left
|
|
mergedReturnStates[k] = payload
|
|
} else { // ax+ay -> a'[x,y]
|
|
mergedParent := merge(aParent, bParent, rootIsWildcard, mergeCache)
|
|
mergedParents[k] = mergedParent
|
|
mergedReturnStates[k] = payload
|
|
}
|
|
i++ // hop over left one as usual
|
|
j++ // but also Skip one in right side since we merge
|
|
} else if a.returnStates[i] < b.returnStates[j] { // copy a[i] to M
|
|
mergedParents[k] = aParent
|
|
mergedReturnStates[k] = a.returnStates[i]
|
|
i++
|
|
} else { // b > a, copy b[j] to M
|
|
mergedParents[k] = bParent
|
|
mergedReturnStates[k] = b.returnStates[j]
|
|
j++
|
|
}
|
|
k++
|
|
}
|
|
// copy over any payloads remaining in either array
|
|
if i < len(a.returnStates) {
|
|
for p := i; p < len(a.returnStates); p++ {
|
|
mergedParents[k] = a.parents[p]
|
|
mergedReturnStates[k] = a.returnStates[p]
|
|
k++
|
|
}
|
|
} else {
|
|
for p := j; p < len(b.returnStates); p++ {
|
|
mergedParents[k] = b.parents[p]
|
|
mergedReturnStates[k] = b.returnStates[p]
|
|
k++
|
|
}
|
|
}
|
|
// trim merged if we combined a few that had same stack tops
|
|
if k < len(mergedParents) { // write index < last position trim
|
|
if k == 1 { // for just one merged element, return singleton top
|
|
pc := SingletonBasePredictionContextCreate(mergedParents[0], mergedReturnStates[0])
|
|
if mergeCache != nil {
|
|
mergeCache.Put(a, b, pc)
|
|
}
|
|
return pc
|
|
}
|
|
mergedParents = mergedParents[0:k]
|
|
mergedReturnStates = mergedReturnStates[0:k]
|
|
}
|
|
|
|
M := NewArrayPredictionContext(mergedParents, mergedReturnStates)
|
|
|
|
// if we created same array as a or b, return that instead
|
|
// TODO: JI track whether this is possible above during merge sort for speed and possibly avoid an allocation
|
|
if M.Equals(a) {
|
|
if mergeCache != nil {
|
|
mergeCache.Put(a, b, a)
|
|
}
|
|
if runtimeConfig.parserATNSimulatorTraceATNSim {
|
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> a")
|
|
}
|
|
return a
|
|
}
|
|
if M.Equals(b) {
|
|
if mergeCache != nil {
|
|
mergeCache.Put(a, b, b)
|
|
}
|
|
if runtimeConfig.parserATNSimulatorTraceATNSim {
|
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> b")
|
|
}
|
|
return b
|
|
}
|
|
combineCommonParents(&mergedParents)
|
|
|
|
if mergeCache != nil {
|
|
mergeCache.Put(a, b, M)
|
|
}
|
|
if runtimeConfig.parserATNSimulatorTraceATNSim {
|
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> " + M.String())
|
|
}
|
|
return M
|
|
}
|
|
|
|
// Make pass over all M parents and merge any Equals() ones.
|
|
// Note that we pass a pointer to the slice as we want to modify it in place.
|
|
//
|
|
//goland:noinspection GoUnusedFunction
|
|
func combineCommonParents(parents *[]*PredictionContext) {
|
|
uniqueParents := NewJStore[*PredictionContext, Comparator[*PredictionContext]](pContextEqInst, PredictionContextCollection, "combineCommonParents for PredictionContext")
|
|
|
|
for p := 0; p < len(*parents); p++ {
|
|
parent := (*parents)[p]
|
|
_, _ = uniqueParents.Put(parent)
|
|
}
|
|
for q := 0; q < len(*parents); q++ {
|
|
pc, _ := uniqueParents.Get((*parents)[q])
|
|
(*parents)[q] = pc
|
|
}
|
|
}
|
|
|
|
func getCachedBasePredictionContext(context *PredictionContext, contextCache *PredictionContextCache, visited *VisitRecord) *PredictionContext {
|
|
if context.isEmpty() {
|
|
return context
|
|
}
|
|
existing, present := visited.Get(context)
|
|
if present {
|
|
return existing
|
|
}
|
|
|
|
existing, present = contextCache.Get(context)
|
|
if present {
|
|
visited.Put(context, existing)
|
|
return existing
|
|
}
|
|
changed := false
|
|
parents := make([]*PredictionContext, context.length())
|
|
for i := 0; i < len(parents); i++ {
|
|
parent := getCachedBasePredictionContext(context.GetParent(i), contextCache, visited)
|
|
if changed || !parent.Equals(context.GetParent(i)) {
|
|
if !changed {
|
|
parents = make([]*PredictionContext, context.length())
|
|
for j := 0; j < context.length(); j++ {
|
|
parents[j] = context.GetParent(j)
|
|
}
|
|
changed = true
|
|
}
|
|
parents[i] = parent
|
|
}
|
|
}
|
|
if !changed {
|
|
contextCache.add(context)
|
|
visited.Put(context, context)
|
|
return context
|
|
}
|
|
var updated *PredictionContext
|
|
if len(parents) == 0 {
|
|
updated = BasePredictionContextEMPTY
|
|
} else if len(parents) == 1 {
|
|
updated = SingletonBasePredictionContextCreate(parents[0], context.getReturnState(0))
|
|
} else {
|
|
updated = NewArrayPredictionContext(parents, context.GetReturnStates())
|
|
}
|
|
contextCache.add(updated)
|
|
visited.Put(updated, updated)
|
|
visited.Put(context, updated)
|
|
|
|
return updated
|
|
}
|