package sensitive import "sort" // ByteInterval is a span in the original UTF-8 text. type ByteInterval struct { From int To int } // IntervalsFromMatches maps normalized matches back to original byte intervals. func IntervalsFromMatches(idx *Index, matches []Match) ([]ByteInterval, bool) { if idx == nil || len(matches) == 0 { return nil, true } intervals := make([]ByteInterval, 0, len(matches)) for i := range matches { m := matches[i] if m.Start < 0 || m.End <= m.Start || m.End > len(idx.OrigFrom) { return nil, false } last := m.End - 1 intervals = append(intervals, ByteInterval{ From: idx.OrigFrom[m.Start], To: idx.OrigTo[last], }) } return mergeIntervals(intervals), true } func mergeIntervals(in []ByteInterval) []ByteInterval { if len(in) == 0 { return nil } sort.Slice(in, func(i, j int) bool { return in[i].From < in[j].From }) out := []ByteInterval{in[0]} for i := 1; i < len(in); i++ { last := &out[len(out)-1] cur := in[i] if cur.From <= last.To { if cur.To > last.To { last.To = cur.To } continue } out = append(out, cur) } return out } // Mask replaces each interval with repl (default "***"). func Mask(orig []byte, intervals []ByteInterval, repl string) string { if len(intervals) == 0 { return string(orig) } if repl == "" { repl = "***" } var b []byte pos := 0 for _, iv := range intervals { if iv.From < pos || iv.To < iv.From || iv.To > len(orig) { return string(orig) } b = append(b, orig[pos:iv.From]...) b = append(b, repl...) pos = iv.To } b = append(b, orig[pos:]...) return string(b) } // Excerpt returns a short slice of orig around [from,to). func Excerpt(orig []byte, from, to int, maxLen int) string { if maxLen <= 0 { maxLen = 128 } if from < 0 { from = 0 } if to > len(orig) { to = len(orig) } if from >= to { return "" } const pad = 24 start := from - pad if start < 0 { start = 0 } end := to + pad if end > len(orig) { end = len(orig) } s := string(orig[start:end]) if len(s) > maxLen { if maxLen <= 3 { return s[:maxLen] } return s[:maxLen-3] + "..." } return s }