diff --git a/array.go b/array.go index 03d35617..66f672f9 100644 --- a/array.go +++ b/array.go @@ -2725,8 +2725,39 @@ func (a *Array) setCallbackWithChild(i uint64, child Value) { return err } - if existingValueStorable == nil { - return NewFatalError(fmt.Errorf("failed to reset child value in parent updater callback because previous value is nil")) + // Verify overwritten storable has identical value ID. + + switch x := existingValueStorable.(type) { + case SlabIDStorable: + sid := SlabID(x) + if !vid.equal(sid) { + return NewFatalError( + fmt.Errorf( + "failed to reset child value in parent updater callback: overwritten SlabIDStorable %s != value ID %s", + sid, + vid)) + } + + case Slab: + sid := x.SlabID() + if !vid.equal(sid) { + return NewFatalError( + fmt.Errorf( + "failed to reset child value in parent updater callback: overwritten Slab ID %s != value ID %s", + sid, + vid)) + } + + case nil: + return NewFatalError( + fmt.Errorf( + "failed to reset child value in parent updater callback: overwritten value is nil")) + + default: + return NewFatalError( + fmt.Errorf( + "failed to reset child value in parent updater callback: overwritten value is wrong type %T", + existingValueStorable)) } return nil diff --git a/map.go b/map.go index f9f6b596..4aa900e0 100644 --- a/map.go +++ b/map.go @@ -4523,6 +4523,8 @@ func (m *OrderedMap) setCallbackWithChild( return } + vid := c.ValueID() + c.setParentUpdater(func() error { // Set child value with parent map using same key. // Set() calls c.Storable() which returns inlined or not-inlined child storable. @@ -4531,10 +4533,40 @@ func (m *OrderedMap) setCallbackWithChild( return err } - if existingValueStorable == nil { - return NewFatalError(fmt.Errorf("failed to reset child value in parent updater callback because previous value is nil")) - } + // Verify overwritten storable has identical value ID. + + switch x := existingValueStorable.(type) { + case SlabIDStorable: + sid := SlabID(x) + if !vid.equal(sid) { + return NewFatalError( + fmt.Errorf( + "failed to reset child value in parent updater callback: overwritten SlabIDStorable %s != value ID %s", + sid, + vid)) + } + + case Slab: + sid := x.SlabID() + if !vid.equal(sid) { + return NewFatalError( + fmt.Errorf( + "failed to reset child value in parent updater callback: overwritten Slab ID %s != value ID %s", + sid, + vid)) + } + + case nil: + return NewFatalError( + fmt.Errorf( + "failed to reset child value in parent updater callback: overwritten value is nil")) + default: + return NewFatalError( + fmt.Errorf( + "failed to reset child value in parent updater callback: overwritten value is wrong type %T", + existingValueStorable)) + } return nil }) } diff --git a/storage.go b/storage.go index 7deb5b60..71d1804b 100644 --- a/storage.go +++ b/storage.go @@ -41,6 +41,19 @@ func slabIDToValueID(sid SlabID) ValueID { return id } +func (vid ValueID) equal(sid SlabID) bool { + return bytes.Equal(vid[:8], sid.address[:]) && + bytes.Equal(vid[8:], sid.index[:]) +} + +func (vid ValueID) String() string { + return fmt.Sprintf( + "0x%x.%d", + binary.BigEndian.Uint64(vid[:8]), + binary.BigEndian.Uint64(vid[8:]), + ) +} + type ( Address [8]byte SlabIndex [8]byte