Files
prysm/encoding/ssz/query/query.go
fernantho d3bd0eaa30 SSZ-QL: update "path parsing" data types (#15935)
* updated path processing data types, refactored ParsePath and fixed tests

* updated generalized index accordingly, changed input parameter path type from []PathElemen to Path

* updated query.go accordingly, changed input parameter path type from []PathElemen to Path

* added descriptive changelog

* Update encoding/ssz/query/path.go

Co-authored-by: Jun Song <87601811+syjn99@users.noreply.github.com>

* Added documentation for Path struct and renamed  to  for clarity

* Update encoding/ssz/query/path.go

Co-authored-by: Radosław Kapka <radoslaw.kapka@gmail.com>

* updated changelog to its correct type: Changed

* updated outdated comment in generalized_index.go and removed test in generalized_index_test.go as this one belongs in path_test.go

* Added validateRawPath with strict raw-path validation only - no raw-path fixing is added. Added test suite covering

* added extra tests for wrongly formated paths

---------

Co-authored-by: Jun Song <87601811+syjn99@users.noreply.github.com>
Co-authored-by: Radosław Kapka <radoslaw.kapka@gmail.com>
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2025-10-31 17:37:59 +00:00

84 lines
2.6 KiB
Go

package query
import (
"errors"
"fmt"
)
// CalculateOffsetAndLength calculates the offset and length of a given path within the SSZ object.
// By walking the given path, it accumulates the offsets based on SszInfo.
func CalculateOffsetAndLength(sszInfo *SszInfo, path Path) (*SszInfo, uint64, uint64, error) {
if sszInfo == nil {
return nil, 0, 0, errors.New("sszInfo is nil")
}
if len(path.Elements) == 0 {
return nil, 0, 0, errors.New("path is empty")
}
walk := sszInfo
offset := uint64(0)
for pathIndex, elem := range path.Elements {
containerInfo, err := walk.ContainerInfo()
if err != nil {
return nil, 0, 0, fmt.Errorf("could not get field infos: %w", err)
}
fieldInfo, exists := containerInfo.fields[elem.Name]
if !exists {
return nil, 0, 0, fmt.Errorf("field %s not found in containerInfo", elem.Name)
}
offset += fieldInfo.offset
walk = fieldInfo.sszInfo
// Check for accessing List/Vector elements by index
if elem.Index != nil {
switch walk.sszType {
case List:
index := *elem.Index
listInfo := walk.listInfo
if index >= listInfo.length {
return nil, 0, 0, fmt.Errorf("index %d out of bounds for field %s with size %d", index, elem.Name, listInfo.length)
}
walk = listInfo.element
if walk.isVariable {
// Cumulative sum of sizes of previous elements to get the offset.
for i := range index {
offset += listInfo.elementSizes[i]
}
// NOTE: When populating recursively, the shared element template is updated for each
// list item, causing it to retain the size information of the last processed element.
// This wouldn't be an issue if this is in the middle of the path, as the walk would be updated
// to the next field's sszInfo, which would have the correct size information.
// However, if this is the last element in the path, we need to ensure we return the correct size
// for the indexed element. Hence, we return the size from elementSizes.
if pathIndex == len(path.Elements)-1 {
return walk, offset, listInfo.elementSizes[index], nil
}
} else {
offset += index * listInfo.element.Size()
}
case Vector:
index := *elem.Index
vectorInfo := walk.vectorInfo
if index >= vectorInfo.length {
return nil, 0, 0, fmt.Errorf("index %d out of bounds for field %s with size %d", index, elem.Name, vectorInfo.length)
}
offset += index * vectorInfo.element.Size()
walk = vectorInfo.element
default:
return nil, 0, 0, fmt.Errorf("field %s of type %s does not support index access", elem.Name, walk.sszType)
}
}
}
return walk, offset, walk.Size(), nil
}