diff_adt.go 3.91 KB
Newer Older
董子豪's avatar
董子豪 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
package adt

import (
	"bytes"

	"github.com/filecoin-project/go-state-types/abi"
	typegen "github.com/whyrusleeping/cbor-gen"
)

// 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)
	})
}