package adt import ( "bytes" typegen "github.com/whyrusleeping/cbor-gen" "github.com/filecoin-project/go-state-types/abi" ) // AdtArrayDiff generalizes adt.Array diffing by accepting a Deferred type that can unmarshalled to its corresponding struct // in an interface implantation. // Add should be called when a new k,v is added to the array // Modify should be called when a value is modified in the array // Remove should be called when a value is removed from the array type AdtArrayDiff interface { Add(key uint64, val *typegen.Deferred) error Modify(key uint64, from, to *typegen.Deferred) error Remove(key uint64, val *typegen.Deferred) error } // TODO Performance can be improved by diffing the underlying IPLD graph, e.g. https://github.com/ipfs/go-merkledag/blob/749fd8717d46b4f34c9ce08253070079c89bc56d/dagutils/diff.go#L104 // CBOR Marshaling will likely be the largest performance bottleneck here. // DiffAdtArray accepts two *adt.Array's and an AdtArrayDiff implementation. It does the following: // - All values that exist in preArr and not in curArr are passed to AdtArrayDiff.Remove() // - All values that exist in curArr nnd not in prevArr are passed to adtArrayDiff.Add() // - All values that exist in preArr and in curArr are passed to AdtArrayDiff.Modify() // - It is the responsibility of AdtArrayDiff.Modify() to determine if the values it was passed have been modified. func DiffAdtArray(preArr, curArr Array, out AdtArrayDiff) error { notNew := make(map[int64]struct{}, curArr.Length()) prevVal := new(typegen.Deferred) if err := preArr.ForEach(prevVal, func(i int64) error { curVal := new(typegen.Deferred) found, err := curArr.Get(uint64(i), curVal) if err != nil { return err } if !found { if err := out.Remove(uint64(i), prevVal); err != nil { return err } return nil } // no modification if !bytes.Equal(prevVal.Raw, curVal.Raw) { if err := out.Modify(uint64(i), prevVal, curVal); err != nil { return err } } notNew[i] = struct{}{} return nil }); err != nil { return err } curVal := new(typegen.Deferred) return curArr.ForEach(curVal, func(i int64) error { if _, ok := notNew[i]; ok { return nil } return out.Add(uint64(i), curVal) }) } // TODO Performance can be improved by diffing the underlying IPLD graph, e.g. https://github.com/ipfs/go-merkledag/blob/749fd8717d46b4f34c9ce08253070079c89bc56d/dagutils/diff.go#L104 // CBOR Marshaling will likely be the largest performance bottleneck here. // AdtMapDiff generalizes adt.Map diffing by accepting a Deferred type that can unmarshalled to its corresponding struct // in an interface implantation. // AsKey should return the Keyer implementation specific to the map // Add should be called when a new k,v is added to the map // Modify should be called when a value is modified in the map // Remove should be called when a value is removed from the map type AdtMapDiff interface { AsKey(key string) (abi.Keyer, error) Add(key string, val *typegen.Deferred) error Modify(key string, from, to *typegen.Deferred) error Remove(key string, val *typegen.Deferred) error } func DiffAdtMap(preMap, curMap Map, out AdtMapDiff) error { notNew := make(map[string]struct{}) prevVal := new(typegen.Deferred) if err := preMap.ForEach(prevVal, func(key string) error { curVal := new(typegen.Deferred) k, err := out.AsKey(key) if err != nil { return err } found, err := curMap.Get(k, curVal) if err != nil { return err } if !found { if err := out.Remove(key, prevVal); err != nil { return err } return nil } // no modification if !bytes.Equal(prevVal.Raw, curVal.Raw) { if err := out.Modify(key, prevVal, curVal); err != nil { return err } } notNew[key] = struct{}{} return nil }); err != nil { return err } curVal := new(typegen.Deferred) return curMap.ForEach(curVal, func(key string) error { if _, ok := notNew[key]; ok { return nil } return out.Add(key, curVal) }) }