mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
working on caching transferelems
This commit is contained in:
parent
fb641ac717
commit
f8b8e757fc
@ -531,87 +531,6 @@ func (r *RootElem) MakeVDom() *VDomElem {
|
||||
return r.makeVDom(r.Root)
|
||||
}
|
||||
|
||||
func ConvertElemsToTransferElems(elems []VDomElem) []VDomTransferElem {
|
||||
var transferElems []VDomTransferElem
|
||||
textCounter := 0 // Counter for generating unique IDs for #text nodes
|
||||
|
||||
// Helper function to recursively process each VDomElem in preorder
|
||||
var processElem func(elem VDomElem) string
|
||||
processElem = func(elem VDomElem) string {
|
||||
// Handle #text nodes by generating a unique placeholder ID
|
||||
if elem.Tag == "#text" {
|
||||
textId := fmt.Sprintf("text-%d", textCounter)
|
||||
textCounter++
|
||||
transferElems = append(transferElems, VDomTransferElem{
|
||||
WaveId: textId,
|
||||
Tag: elem.Tag,
|
||||
Text: elem.Text,
|
||||
Props: nil,
|
||||
Children: nil,
|
||||
})
|
||||
return textId
|
||||
}
|
||||
|
||||
// Convert children to WaveId references, handling potential #text nodes
|
||||
childrenIds := make([]string, len(elem.Children))
|
||||
for i, child := range elem.Children {
|
||||
childrenIds[i] = processElem(child) // Children are not roots
|
||||
}
|
||||
|
||||
// Create the VDomTransferElem for the current element
|
||||
transferElem := VDomTransferElem{
|
||||
WaveId: elem.WaveId,
|
||||
Tag: elem.Tag,
|
||||
Props: elem.Props,
|
||||
Children: childrenIds,
|
||||
Text: elem.Text,
|
||||
}
|
||||
transferElems = append(transferElems, transferElem)
|
||||
|
||||
return elem.WaveId
|
||||
}
|
||||
|
||||
// Start processing each top-level element, marking them as roots
|
||||
for _, elem := range elems {
|
||||
processElem(elem)
|
||||
}
|
||||
|
||||
return transferElems
|
||||
}
|
||||
|
||||
func DedupTransferElems(elems []VDomTransferElem) []VDomTransferElem {
|
||||
seen := make(map[string]int) // maps WaveId to its index in the result slice
|
||||
var result []VDomTransferElem
|
||||
|
||||
for _, elem := range elems {
|
||||
if idx, exists := seen[elem.WaveId]; exists {
|
||||
// Overwrite the previous element with the latest one
|
||||
result[idx] = elem
|
||||
} else {
|
||||
// Add new element and store its index
|
||||
seen[elem.WaveId] = len(result)
|
||||
result = append(result, elem)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (beUpdate *VDomBackendUpdate) CreateTransferElems() {
|
||||
var vdomElems []VDomElem
|
||||
for idx, reUpdate := range beUpdate.RenderUpdates {
|
||||
if reUpdate.VDom == nil {
|
||||
continue
|
||||
}
|
||||
vdomElems = append(vdomElems, *reUpdate.VDom)
|
||||
beUpdate.RenderUpdates[idx].VDomWaveId = reUpdate.VDom.WaveId
|
||||
beUpdate.RenderUpdates[idx].VDom = nil
|
||||
}
|
||||
transferElems := ConvertElemsToTransferElems(vdomElems)
|
||||
transferElems = DedupTransferElems(transferElems)
|
||||
beUpdate.TransferElems = transferElems
|
||||
}
|
||||
|
||||
// SplitBackendUpdate splits a large VDomBackendUpdate into multiple smaller updates
|
||||
// The first update contains all the core fields, while subsequent updates only contain
|
||||
// array elements that need to be appended
|
||||
|
@ -55,6 +55,9 @@ type Client struct {
|
||||
OverrideUrlHandler http.Handler
|
||||
NewBlockFlag bool
|
||||
SetupFn func()
|
||||
TransferElemCache map[string][]byte
|
||||
TextNodeCache map[string]int
|
||||
TextNodeNextId int
|
||||
}
|
||||
|
||||
func (c *Client) GetIsDone() bool {
|
||||
@ -90,11 +93,14 @@ func MakeClient(appOpts AppOpts) *Client {
|
||||
appOpts.NewBlockFlag = "n"
|
||||
}
|
||||
client := &Client{
|
||||
Lock: &sync.Mutex{},
|
||||
AppOpts: appOpts,
|
||||
Root: vdom.MakeRoot(),
|
||||
DoneCh: make(chan struct{}),
|
||||
UrlHandlerMux: mux.NewRouter(),
|
||||
Lock: &sync.Mutex{},
|
||||
AppOpts: appOpts,
|
||||
Root: vdom.MakeRoot(),
|
||||
DoneCh: make(chan struct{}),
|
||||
UrlHandlerMux: mux.NewRouter(),
|
||||
TransferElemCache: make(map[string][]byte),
|
||||
TextNodeCache: make(map[string]int),
|
||||
TextNodeNextId: 1,
|
||||
Opts: vdom.VDomBackendOpts{
|
||||
CloseOnCtrlC: appOpts.CloseOnCtrlC,
|
||||
GlobalKeyboardEvents: appOpts.GlobalKeyboardEvents,
|
||||
|
@ -75,7 +75,7 @@ func (impl *VDomServerImpl) VDomRenderCommand(ctx context.Context, feUpdate vdom
|
||||
} else {
|
||||
update, err = impl.Client.incrementalRender()
|
||||
}
|
||||
update.CreateTransferElems()
|
||||
impl.Client.CreateTransferElems(update)
|
||||
|
||||
if err != nil {
|
||||
respChan <- wshrpc.RespOrErrorUnion[*vdom.VDomBackendUpdate]{
|
||||
|
139
pkg/vdom/vdomclient/vdomtransfer.go
Normal file
139
pkg/vdom/vdomclient/vdomtransfer.go
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package vdomclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/wavetermdev/waveterm/pkg/vdom"
|
||||
)
|
||||
|
||||
func transferElemsEqual(t1 *vdom.VDomTransferElem, t2 *vdom.VDomTransferElem) bool {
|
||||
if t1 == nil || t2 == nil {
|
||||
return false
|
||||
}
|
||||
if t1.WaveId != t2.WaveId || t1.Tag != t2.Tag || t1.Text != t2.Text {
|
||||
return false
|
||||
}
|
||||
if len(t1.Children) != len(t2.Children) {
|
||||
return false
|
||||
}
|
||||
for i := range t1.Children {
|
||||
if t1.Children[i] != t2.Children[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Client) ConvertElemsToTransferElems(elems []vdom.VDomElem) []vdom.VDomTransferElem {
|
||||
var transferElems []vdom.VDomTransferElem
|
||||
var textCacheHits int
|
||||
var teCacheHits int
|
||||
var numTextNodes int
|
||||
|
||||
// Helper function to recursively process each VDomElem in preorder
|
||||
var processElem func(elem vdom.VDomElem) string
|
||||
processElem = func(elem vdom.VDomElem) string {
|
||||
// Handle #text nodes by generating a unique placeholder ID
|
||||
if elem.Tag == "#text" {
|
||||
textId := c.TextNodeCache[elem.Text]
|
||||
if textId == 0 {
|
||||
textId = c.TextNodeNextId
|
||||
c.TextNodeNextId++
|
||||
c.TextNodeCache[elem.Text] = textId
|
||||
} else {
|
||||
textCacheHits++
|
||||
}
|
||||
textIdStr := fmt.Sprintf("text-%d", textId)
|
||||
transferElems = append(transferElems, vdom.VDomTransferElem{
|
||||
WaveId: textIdStr,
|
||||
Tag: elem.Tag,
|
||||
Text: elem.Text,
|
||||
Props: nil,
|
||||
Children: nil,
|
||||
})
|
||||
return textIdStr
|
||||
}
|
||||
|
||||
// Convert children to WaveId references, handling potential #text nodes
|
||||
childrenIds := make([]string, len(elem.Children))
|
||||
for i, child := range elem.Children {
|
||||
childrenIds[i] = processElem(child) // Children are not roots
|
||||
}
|
||||
|
||||
// Create the VDomTransferElem for the current element
|
||||
transferElem := vdom.VDomTransferElem{
|
||||
WaveId: elem.WaveId,
|
||||
Tag: elem.Tag,
|
||||
Props: elem.Props,
|
||||
Children: childrenIds,
|
||||
Text: elem.Text,
|
||||
}
|
||||
transferElems = append(transferElems, transferElem)
|
||||
|
||||
return elem.WaveId
|
||||
}
|
||||
|
||||
// Start processing each top-level element, marking them as roots
|
||||
for _, elem := range elems {
|
||||
processElem(elem)
|
||||
}
|
||||
|
||||
for _, te := range transferElems {
|
||||
if te.Tag == "#text" {
|
||||
numTextNodes++
|
||||
continue
|
||||
}
|
||||
if te.WaveId == "" {
|
||||
continue
|
||||
}
|
||||
curTe := c.TransferElemCache[te.WaveId]
|
||||
teBytes, _ := json.Marshal(te)
|
||||
if bytes.Equal(curTe, teBytes) {
|
||||
teCacheHits++
|
||||
} else {
|
||||
c.TransferElemCache[te.WaveId] = teBytes
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Converted, transferelems: %d/%d, textcache: %d/%d\n", teCacheHits, len(transferElems)-numTextNodes, textCacheHits, numTextNodes)
|
||||
return transferElems
|
||||
}
|
||||
|
||||
func (c *Client) DedupTransferElems(elems []vdom.VDomTransferElem) []vdom.VDomTransferElem {
|
||||
seen := make(map[string]int) // maps WaveId to its index in the result slice
|
||||
var result []vdom.VDomTransferElem
|
||||
|
||||
for _, elem := range elems {
|
||||
if idx, exists := seen[elem.WaveId]; exists {
|
||||
// Overwrite the previous element with the latest one
|
||||
result[idx] = elem
|
||||
} else {
|
||||
// Add new element and store its index
|
||||
seen[elem.WaveId] = len(result)
|
||||
result = append(result, elem)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *Client) CreateTransferElems(beUpdate *vdom.VDomBackendUpdate) {
|
||||
var vdomElems []vdom.VDomElem
|
||||
for idx, reUpdate := range beUpdate.RenderUpdates {
|
||||
if reUpdate.VDom == nil {
|
||||
continue
|
||||
}
|
||||
vdomElems = append(vdomElems, *reUpdate.VDom)
|
||||
beUpdate.RenderUpdates[idx].VDomWaveId = reUpdate.VDom.WaveId
|
||||
beUpdate.RenderUpdates[idx].VDom = nil
|
||||
}
|
||||
transferElems := c.ConvertElemsToTransferElems(vdomElems)
|
||||
transferElems = c.DedupTransferElems(transferElems)
|
||||
beUpdate.TransferElems = transferElems
|
||||
}
|
Loading…
Reference in New Issue
Block a user