github.com/nikandfor/tlog@v0.21.3/convert/rewriter.go (about) 1 package convert 2 3 import ( 4 "bytes" 5 "errors" 6 "io" 7 "sort" 8 9 "github.com/nikandfor/tlog" 10 "github.com/nikandfor/tlog/tlwire" 11 ) 12 13 type ( 14 Rewriter struct { 15 io.Writer 16 tlwire.Decoder 17 tlwire.Encoder 18 19 Rule RewriterRule 20 21 b []byte 22 } 23 24 RewriterRule interface { 25 Rewrite(b, p []byte, path []tlog.RawMessage, kst, st int) ([]byte, int, error) 26 } 27 28 RewriterFunc func(b, p []byte, path []tlog.RawMessage, kst, st int) ([]byte, int, error) 29 30 KeyRenamer struct { 31 rules []RenameRule 32 33 d tlwire.Decoder 34 e tlwire.Encoder 35 36 Rewriter RewriterRule 37 Fallback RewriterRule 38 } 39 40 RenameRule struct { 41 Path []tlog.RawMessage 42 43 Rename []byte 44 Prefix []byte 45 Remove bool 46 } 47 ) 48 49 var ErrFallback = errors.New("fallback") 50 51 func NewRewriter(w io.Writer) *Rewriter { 52 return &Rewriter{Writer: w} 53 } 54 55 func (w *Rewriter) Write(p []byte) (n int, err error) { 56 w.b, n, err = w.Rewrite(w.b[:0], p, nil, -1, 0) 57 if err != nil { 58 return 0, err 59 } 60 61 if w.Writer != nil { 62 _, err = w.Writer.Write(w.b) 63 } 64 65 return 66 } 67 68 func (w *Rewriter) Rewrite(b, p []byte, path []tlog.RawMessage, kst, st int) (r []byte, i int, err error) { 69 if w.Rule != nil { 70 r, i, err = w.Rule.Rewrite(b, p, path, kst, st) 71 if !errors.Is(err, ErrFallback) { 72 return 73 } 74 } 75 76 if st == len(p) { 77 return b, st, nil 78 } 79 80 tag, sub, i := w.Tag(p, st) 81 82 if kst != -1 && tag != tlwire.Semantic { 83 b = append(b, p[kst:st]...) 84 } 85 86 switch tag { 87 case tlwire.Int, tlwire.Neg: 88 case tlwire.String, tlwire.Bytes: 89 i = w.Skip(p, st) 90 case tlwire.Array, tlwire.Map: 91 b = append(b, p[st:i]...) 92 kst := -1 93 subp := path 94 95 if tag == tlwire.Array { 96 subp = append(subp, []byte{tlwire.Array}) 97 } 98 99 for el := 0; sub == -1 || el < int(sub); el++ { 100 if sub == -1 && w.Break(p, &i) { 101 break 102 } 103 104 if tag == tlwire.Map { 105 kst = i 106 _, i = w.Bytes(p, i) 107 108 subp = append(subp[:len(path)], p[kst:i]) 109 } 110 111 b, i, err = w.Rewrite(b, p, subp, kst, i) 112 if err != nil { 113 return 114 } 115 } 116 117 if sub == -1 { 118 b = w.AppendBreak(b) 119 } 120 121 return b, i, nil 122 case tlwire.Semantic: 123 path = append(path, p[st:i]) 124 125 return w.Rewrite(b, p, path, kst, i) 126 case tlwire.Special: 127 switch sub { 128 case tlwire.False, 129 tlwire.True, 130 tlwire.Nil, 131 tlwire.Undefined, 132 tlwire.None, 133 tlwire.Break: 134 case tlwire.Float8: 135 i += 1 136 case tlwire.Float16: 137 i += 2 138 case tlwire.Float32: 139 i += 4 140 case tlwire.Float64: 141 i += 8 142 default: 143 panic("unsupported special") 144 } 145 } 146 147 b = append(b, p[st:i]...) 148 149 return b, i, nil 150 } 151 152 func (f RewriterFunc) Rewrite(b, p []byte, path []tlog.RawMessage, kst, st int) ([]byte, int, error) { 153 return f(b, p, path, kst, st) 154 } 155 156 func NewKeyRenamer(rew RewriterRule, rules ...RenameRule) *KeyRenamer { 157 w := &KeyRenamer{ 158 Rewriter: rew, 159 } 160 161 w.Append(rules...) 162 163 return w 164 } 165 166 func (w *KeyRenamer) Append(rules ...RenameRule) { 167 w.rules = append(w.rules, rules...) 168 169 sort.Slice(w.rules, func(i, j int) bool { 170 return w.cmp(w.rules[i].Path, w.rules[j].Path) < 0 171 }) 172 } 173 174 func (w *KeyRenamer) Rewrite(b, p []byte, path []tlog.RawMessage, kst, st int) ([]byte, int, error) { 175 pos := sort.Search(len(w.rules), func(i int) bool { 176 rule := w.rules[i] 177 178 return w.cmp(path, rule.Path) <= 0 179 }) 180 181 // fmt.Printf("rewrite %q -> %v %v\n", path, pos, pos < len(w.rules) && w.cmp(path, w.rules[pos].Path) == 0) 182 183 if pos == len(w.rules) || w.cmp(path, w.rules[pos].Path) != 0 { 184 return w.fallback(b, p, path, kst, st) 185 } 186 187 rule := w.rules[pos] 188 189 if rule.Remove { 190 end := w.d.Skip(p, st) 191 return b, end, nil 192 } 193 194 key, kend := w.d.Bytes(p, kst) 195 196 l := len(rule.Prefix) 197 198 if rule.Rename != nil { 199 l += len(rule.Rename) 200 } else { 201 l += len(key) 202 } 203 204 b = w.e.AppendTag(b, tlwire.String, l) 205 b = append(b, rule.Prefix...) 206 207 if rule.Rename != nil { 208 b = append(b, rule.Rename...) 209 } else { 210 b = append(b, key...) 211 } 212 213 b = append(b, p[kend:st]...) 214 215 if w.Rewriter != nil { 216 return w.Rewriter.Rewrite(b, p, path, -1, st) 217 } 218 219 end := w.d.Skip(p, st) 220 b = append(b, p[st:end]...) 221 222 return b, end, nil 223 } 224 225 func (w *KeyRenamer) fallback(b, p []byte, path []tlog.RawMessage, kst, st int) ([]byte, int, error) { 226 if w.Fallback == nil { 227 return b, st, ErrFallback 228 } 229 230 return w.Fallback.Rewrite(b, p, path, kst, st) 231 } 232 233 func (w *KeyRenamer) cmp(x, y []tlog.RawMessage) (r int) { 234 // defer func() { 235 // fmt.Printf("cmp %q %q -> %d from %v\n", x, y, r, loc.Caller(1)) 236 // }() 237 for i := 0; i < min(len(x), len(y)); i++ { 238 r = bytes.Compare(x[i], y[i]) 239 if r != 0 { 240 return r 241 } 242 } 243 244 if len(x) != len(y) { 245 if len(x) < len(y) { 246 return -1 247 } 248 249 return 1 250 } 251 252 return 0 253 } 254 255 func min(a, b int) int { 256 if a < b { 257 return a 258 } 259 260 return b 261 }