func diffLists(dq *diffQueue, w io.Writer, p types.Path, v1, v2 types.List) { wroteHeader := false splices := v2.Diff(v1) for _, splice := range splices { if splice.SpRemoved == splice.SpAdded { for i := uint64(0); i < splice.SpRemoved; i++ { lastEl := v1.Get(splice.SpAt + i) newEl := v2.Get(splice.SpFrom + i) if canCompare(lastEl, newEl) { idx := types.Number(splice.SpAt + i) p1 := p.AddIndex(idx) dq.PushBack(diffInfo{p1, idx, lastEl, newEl}) } else { wroteHeader = writeHeader(w, wroteHeader, p) line(w, subPrefix, nil, v1.Get(splice.SpAt+i)) line(w, addPrefix, nil, v2.Get(splice.SpFrom+i)) } } } else { for i := uint64(0); i < splice.SpRemoved; i++ { wroteHeader = writeHeader(w, wroteHeader, p) line(w, subPrefix, nil, v1.Get(splice.SpAt+i)) } for i := uint64(0); i < splice.SpAdded; i++ { wroteHeader = writeHeader(w, wroteHeader, p) line(w, addPrefix, nil, v2.Get(splice.SpFrom+i)) } } } writeFooter(w, wroteHeader) }
func diffMaps(dq *diffQueue, w io.Writer, p types.Path, v1, v2 types.Map) { wroteHeader := false added, removed, modified := v2.Diff(v1) for _, k := range added { wroteHeader = writeHeader(w, wroteHeader, p) line(w, addPrefix, k, v2.Get(k)) } for _, k := range removed { wroteHeader = writeHeader(w, wroteHeader, p) line(w, subPrefix, k, v1.Get(k)) } for _, k := range modified { c1, c2 := v1.Get(k), v2.Get(k) if canCompare(c1, c2) { buf := bytes.NewBuffer(nil) d.Exp.NoError(types.WriteEncodedValueWithTags(buf, k)) p1 := p.AddField(buf.String()) dq.PushBack(diffInfo{path: p1, key: k, v1: c1, v2: c2}) } else { wroteHeader = writeHeader(w, wroteHeader, p) line(w, subPrefix, k, v1.Get(k)) line(w, addPrefix, k, v2.Get(k)) } } writeFooter(w, wroteHeader) }
func diffStructs(w io.Writer, p types.Path, v1, v2 types.Struct) { changeChan := make(chan types.ValueChanged) stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished defer stop(stopChan) go func() { v2.Diff(v1, changeChan, stopChan) close(changeChan) }() wroteHeader := false for change := range changeChan { fn := string(change.V.(types.String)) switch change.ChangeType { case types.DiffChangeAdded: wroteHeader = writeHeader(w, wroteHeader, p) field(w, ADD, change.V, v2.Get(fn)) case types.DiffChangeRemoved: wroteHeader = writeHeader(w, wroteHeader, p) field(w, DEL, change.V, v1.Get(fn)) case types.DiffChangeModified: f1 := v1.Get(fn) f2 := v2.Get(fn) if shouldDescend(f1, f2) { diff(w, p.AddField(fn), types.String(fn), f1, f2) } else { wroteHeader = writeHeader(w, wroteHeader, p) field(w, DEL, change.V, f1) field(w, ADD, change.V, f2) } } } writeFooter(w, wroteHeader) }
func diffLists(w io.Writer, p types.Path, v1, v2 types.List) (err error) { spliceChan := make(chan types.Splice) stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished go func() { v2.Diff(v1, spliceChan, stopChan) close(spliceChan) }() wroteHdr := false for splice := range spliceChan { if err != nil { break } if splice.SpRemoved == splice.SpAdded { // Heuristic: list only has modifications. for i := uint64(0); i < splice.SpRemoved; i++ { lastEl := v1.Get(splice.SpAt + i) newEl := v2.Get(splice.SpFrom + i) if shouldDescend(lastEl, newEl) { idx := types.Number(splice.SpAt + i) writeFooter(w, &wroteHdr) err = diff(w, p.AddIndex(idx), idx, lastEl, newEl) } else { writeHeader(w, p, &wroteHdr) line(w, DEL, nil, v1.Get(splice.SpAt+i)) err = line(w, ADD, nil, v2.Get(splice.SpFrom+i)) } } continue } // Heuristic: list only has additions/removals. for i := uint64(0); i < splice.SpRemoved && err == nil; i++ { writeHeader(w, p, &wroteHdr) err = line(w, DEL, nil, v1.Get(splice.SpAt+i)) } for i := uint64(0); i < splice.SpAdded && err == nil; i++ { writeHeader(w, p, &wroteHdr) err = line(w, ADD, nil, v2.Get(splice.SpFrom+i)) } } err = writeFooter(w, &wroteHdr) if err != nil { stopChan <- struct{}{} // Wait for diff to stop. for range spliceChan { } } return }
func writeHeader(w io.Writer, wroteHeader bool, p types.Path) bool { var err error if !wroteHeader { _, err = w.Write([]byte(p.String())) d.Exp.NoError(err) _, err = w.Write([]byte(" {\n")) d.Exp.NoError(err) wroteHeader = true } return wroteHeader }
func writeHeader(w io.Writer, p types.Path, wroteHdr *bool) error { if *wroteHdr { return nil } *wroteHdr = true hdr := "(root)" if len(p) > 0 { hdr = p.String() } return write(w, []byte(hdr+" {\n")) }
func writeHeader(w io.Writer, wroteHeader bool, p types.Path) bool { if !wroteHeader { if len(p) == 0 { write(w, []byte("(root)")) } else { write(w, []byte(p.String())) } write(w, []byte(" {\n")) } return true }
func diffOrdered(w io.Writer, p types.Path, lf lineFunc, df diffFunc, kf, v1, v2 valueFunc) (err error) { changeChan := make(chan types.ValueChanged) stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished go func() { df(changeChan, stopChan) close(changeChan) }() wroteHdr := false for change := range changeChan { if err != nil { break } k := kf(change.V) switch change.ChangeType { case types.DiffChangeAdded: writeHeader(w, p, &wroteHdr) err = lf(w, ADD, k, v2(change.V)) case types.DiffChangeRemoved: writeHeader(w, p, &wroteHdr) err = lf(w, DEL, k, v1(change.V)) case types.DiffChangeModified: c1, c2 := v1(change.V), v2(change.V) if shouldDescend(c1, c2) { writeFooter(w, &wroteHdr) err = diff(w, p.AddIndex(k), change.V, c1, c2) } else { writeHeader(w, p, &wroteHdr) lf(w, DEL, k, c1) err = lf(w, ADD, k, c2) } default: panic("unknown change type") } } writeFooter(w, &wroteHdr) if err != nil { stopChan <- struct{}{} // Wait for diff to stop. for range changeChan { } } return }
func diffStructs(dq *diffQueue, w io.Writer, p types.Path, v1, v2 types.Struct) { changed := types.StructDiff(v1, v2) wroteHeader := false for _, field := range changed { f1 := v1.Get(field) f2 := v2.Get(field) if canCompare(f1, f2) { p1 := p.AddField(field) dq.PushBack(diffInfo{path: p1, key: types.String(field), v1: f1, v2: f2}) } else { wroteHeader = writeHeader(w, wroteHeader, p) line(w, subPrefix, types.String(field), f1) line(w, addPrefix, types.String(field), f2) } } }
func diffSets(dq *diffQueue, w io.Writer, p types.Path, v1, v2 types.Set) { wroteHeader := false added, removed := v2.Diff(v1) if len(added) == 1 && len(removed) == 1 && canCompare(added[0], removed[0]) { p1 := p.AddField(added[0].Hash().String()) dq.PushBack(diffInfo{path: p1, key: types.String(""), v1: removed[0], v2: added[0]}) } else { for _, value := range removed { wroteHeader = writeHeader(w, wroteHeader, p) line(w, subPrefix, nil, value) } for _, value := range added { wroteHeader = writeHeader(w, wroteHeader, p) line(w, addPrefix, nil, value) } } writeFooter(w, wroteHeader) return }
func diffLists(w io.Writer, p types.Path, v1, v2 types.List) { spliceChan := make(chan types.Splice) stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished defer stop(stopChan) go func() { v2.Diff(v1, spliceChan, stopChan) close(spliceChan) }() wroteHeader := false for splice := range spliceChan { if splice.SpRemoved == splice.SpAdded { for i := uint64(0); i < splice.SpRemoved; i++ { lastEl := v1.Get(splice.SpAt + i) newEl := v2.Get(splice.SpFrom + i) if shouldDescend(lastEl, newEl) { idx := types.Number(splice.SpAt + i) diff(w, p.AddIndex(idx), idx, lastEl, newEl) } else { wroteHeader = writeHeader(w, wroteHeader, p) line(w, DEL, nil, v1.Get(splice.SpAt+i)) line(w, ADD, nil, v2.Get(splice.SpFrom+i)) } } } else { for i := uint64(0); i < splice.SpRemoved; i++ { wroteHeader = writeHeader(w, wroteHeader, p) line(w, DEL, nil, v1.Get(splice.SpAt+i)) } for i := uint64(0); i < splice.SpAdded; i++ { wroteHeader = writeHeader(w, wroteHeader, p) line(w, ADD, nil, v2.Get(splice.SpFrom+i)) } } } writeFooter(w, wroteHeader) }
func diffMaps(w io.Writer, p types.Path, v1, v2 types.Map) { changeChan := make(chan types.ValueChanged) stopChan := make(chan struct{}, 1) // buffer size of 1, so this won't block if diff already finished defer stop(stopChan) go func() { v2.Diff(v1, changeChan, stopChan) close(changeChan) }() wroteHeader := false for change := range changeChan { switch change.ChangeType { case types.DiffChangeAdded: wroteHeader = writeHeader(w, wroteHeader, p) line(w, ADD, change.V, v2.Get(change.V)) case types.DiffChangeRemoved: wroteHeader = writeHeader(w, wroteHeader, p) line(w, DEL, change.V, v1.Get(change.V)) case types.DiffChangeModified: c1, c2 := v1.Get(change.V), v2.Get(change.V) if shouldDescend(c1, c2) { wroteHeader = writeFooter(w, wroteHeader) diff(w, p.AddIndex(change.V), change.V, c1, c2) } else { wroteHeader = writeHeader(w, wroteHeader, p) line(w, DEL, change.V, c1) line(w, ADD, change.V, c2) } default: panic("unknown change type") } } writeFooter(w, wroteHeader) }