Extend history replay for mapped sequence items
This commit is contained in:
@@ -50,7 +50,7 @@ type historyApplication struct {
|
||||
value *document.Node
|
||||
origin Origin
|
||||
sequences []sequenceHistoryContribution
|
||||
sequencesByPath map[string]sequenceHistoryContribution
|
||||
sequencesByPath map[string]sequenceHistoryContributionIndex
|
||||
}
|
||||
|
||||
func cloneHistoryApplications(values []historyApplication) []historyApplication {
|
||||
@@ -92,7 +92,14 @@ func walkHistoryApplication(application historyApplication, visit func(local, ef
|
||||
for index, child := range node.Sequence {
|
||||
effectiveIndex := index
|
||||
if projected {
|
||||
effectiveIndex += contribution.offset
|
||||
if contribution.mode == sequenceHistoryMapped {
|
||||
effectiveIndex = contribution.outputForInput(index)
|
||||
if effectiveIndex < 0 {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
effectiveIndex += contribution.offset
|
||||
}
|
||||
}
|
||||
walk(child, local.Index(index), effective.Index(effectiveIndex))
|
||||
}
|
||||
@@ -112,7 +119,18 @@ func historyApplicationLocalPath(application historyApplication, effective Path)
|
||||
for _, segment := range relative.segments {
|
||||
if contribution, found := sequenceHistoryAt(application.sequencesByPath, local); found {
|
||||
index, valid := schemaIndex(segment)
|
||||
if !valid || segment.kind == pathSegmentKey || index < contribution.offset || index >= contribution.offset+contribution.length {
|
||||
if !valid || segment.kind == pathSegmentKey {
|
||||
return Path{}, false
|
||||
}
|
||||
if contribution.mode == sequenceHistoryMapped {
|
||||
inputIndex := contribution.inputForOutput(index)
|
||||
if inputIndex < 0 {
|
||||
return Path{}, false
|
||||
}
|
||||
local = local.Index(inputIndex)
|
||||
continue
|
||||
}
|
||||
if index < contribution.offset || index >= contribution.offset+contribution.length {
|
||||
return Path{}, false
|
||||
}
|
||||
local = local.Index(index - contribution.offset)
|
||||
@@ -149,6 +167,16 @@ func applicationOperationPath(application historyApplication, query Path, lookup
|
||||
return container, true
|
||||
}
|
||||
}
|
||||
indexed, _ := sequenceHistoryAt(application.sequencesByPath, contribution.path)
|
||||
if indexed.mode == sequenceHistoryMapped && len(query.segments) > len(container.segments) {
|
||||
outputIndex, valid := schemaIndex(query.segments[len(container.segments)])
|
||||
_, replaced := indexed.replacedOutputs[outputIndex]
|
||||
if valid && replaced {
|
||||
if _, present := documentAtPathWithLookup(application.value, local, lookup); !ok || !present {
|
||||
return container.Index(outputIndex), true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
@@ -213,7 +241,7 @@ func mergeHistoryDocument(target **document.Node, incoming *document.Node, index
|
||||
mergeHistoryDocumentAtPath(target, incoming, Path{}, index, nil)
|
||||
}
|
||||
|
||||
func mergeHistoryDocumentAtPath(target **document.Node, incoming *document.Node, path Path, index *mappingIndex, sequences map[string]sequenceHistoryContribution) {
|
||||
func mergeHistoryDocumentAtPath(target **document.Node, incoming *document.Node, path Path, index *mappingIndex, sequences map[string]sequenceHistoryContributionIndex) {
|
||||
if incoming == nil {
|
||||
return
|
||||
}
|
||||
@@ -238,23 +266,71 @@ func mergeHistoryDocumentAtPath(target **document.Node, incoming *document.Node,
|
||||
}
|
||||
}
|
||||
|
||||
func sequenceHistoryAt(contributions map[string]sequenceHistoryContribution, path Path) (sequenceHistoryContribution, bool) {
|
||||
func sequenceHistoryAt(contributions map[string]sequenceHistoryContributionIndex, path Path) (sequenceHistoryContributionIndex, bool) {
|
||||
contribution, found := contributions[path.String()]
|
||||
return contribution, found
|
||||
}
|
||||
|
||||
func indexSequenceHistoryContributions(values []sequenceHistoryContribution) map[string]sequenceHistoryContribution {
|
||||
type sequenceHistoryContributionIndex struct {
|
||||
sequenceHistoryContribution
|
||||
outputIndexes []int
|
||||
inputIndexes []int
|
||||
replacedOutputs map[int]struct{}
|
||||
}
|
||||
|
||||
func indexSequenceHistoryContributions(values []sequenceHistoryContribution) map[string]sequenceHistoryContributionIndex {
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
index := make(map[string]sequenceHistoryContribution, len(values))
|
||||
index := make(map[string]sequenceHistoryContributionIndex, len(values))
|
||||
for _, value := range values {
|
||||
index[value.path.String()] = value
|
||||
contribution := sequenceHistoryContributionIndex{sequenceHistoryContribution: value}
|
||||
if value.mode == sequenceHistoryMapped {
|
||||
maxOutput := -1
|
||||
for _, projection := range value.items {
|
||||
if projection.outputIndex > maxOutput {
|
||||
maxOutput = projection.outputIndex
|
||||
}
|
||||
}
|
||||
contribution.outputIndexes = make([]int, len(value.items))
|
||||
for inputIndex := range contribution.outputIndexes {
|
||||
contribution.outputIndexes[inputIndex] = -1
|
||||
}
|
||||
contribution.inputIndexes = make([]int, maxOutput+1)
|
||||
for outputIndex := range contribution.inputIndexes {
|
||||
contribution.inputIndexes[outputIndex] = -1
|
||||
}
|
||||
for _, projection := range value.items {
|
||||
if projection.inputIndex >= 0 && projection.inputIndex < len(contribution.outputIndexes) && projection.outputIndex >= 0 && projection.outputIndex < len(contribution.inputIndexes) {
|
||||
contribution.outputIndexes[projection.inputIndex] = projection.outputIndex
|
||||
contribution.inputIndexes[projection.outputIndex] = projection.inputIndex
|
||||
}
|
||||
}
|
||||
contribution.replacedOutputs = make(map[int]struct{}, len(value.replacements))
|
||||
for _, replacement := range value.replacements {
|
||||
contribution.replacedOutputs[replacement.outputIndex] = struct{}{}
|
||||
}
|
||||
}
|
||||
index[value.path.String()] = contribution
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
func applySequenceHistory(target **document.Node, incoming *document.Node, contribution sequenceHistoryContribution) {
|
||||
func (contribution sequenceHistoryContributionIndex) outputForInput(inputIndex int) int {
|
||||
if inputIndex < 0 || inputIndex >= len(contribution.outputIndexes) {
|
||||
return -1
|
||||
}
|
||||
return contribution.outputIndexes[inputIndex]
|
||||
}
|
||||
|
||||
func (contribution sequenceHistoryContributionIndex) inputForOutput(outputIndex int) int {
|
||||
if outputIndex < 0 || outputIndex >= len(contribution.inputIndexes) {
|
||||
return -1
|
||||
}
|
||||
return contribution.inputIndexes[outputIndex]
|
||||
}
|
||||
|
||||
func applySequenceHistory(target **document.Node, incoming *document.Node, contribution sequenceHistoryContributionIndex) {
|
||||
if incoming == nil || incoming.Kind != document.Sequence {
|
||||
return
|
||||
}
|
||||
@@ -262,6 +338,18 @@ func applySequenceHistory(target **document.Node, incoming *document.Node, contr
|
||||
*target = document.Clone(incoming)
|
||||
return
|
||||
}
|
||||
if contribution.mode == sequenceHistoryMapped {
|
||||
for _, projection := range contribution.items {
|
||||
if projection.inputIndex < 0 || projection.inputIndex >= len(incoming.Sequence) || projection.outputIndex < 0 {
|
||||
continue
|
||||
}
|
||||
for len((*target).Sequence) <= projection.outputIndex {
|
||||
(*target).Sequence = append((*target).Sequence, nil)
|
||||
}
|
||||
(*target).Sequence[projection.outputIndex] = document.Clone(incoming.Sequence[projection.inputIndex])
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, item := range incoming.Sequence {
|
||||
(*target).Sequence = append((*target).Sequence, document.Clone(item))
|
||||
}
|
||||
@@ -275,6 +363,8 @@ func cloneSequenceHistoryContributions(values []sequenceHistoryContribution) []s
|
||||
for index, value := range values {
|
||||
cloned[index] = value
|
||||
cloned[index].path = value.path.clone()
|
||||
cloned[index].items = append([]sequenceItemProjection(nil), value.items...)
|
||||
cloned[index].replacements = append([]sequenceItemReplacement(nil), value.replacements...)
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user