203 lines
3.6 KiB
Go
203 lines
3.6 KiB
Go
package jsn
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"hash/maphash"
|
|
)
|
|
|
|
// Replace function replaces key-value pairs provided in the `from` argument with those in the `to` argument
|
|
func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
|
|
if len(from) != len(to) {
|
|
return errors.New("'from' and 'to' must be of the same length")
|
|
}
|
|
|
|
if len(from) == 0 || len(to) == 0 {
|
|
_, err := w.Write(b)
|
|
return err
|
|
}
|
|
|
|
h := maphash.Hash{}
|
|
tmap := make(map[uint64]int, len(from))
|
|
|
|
for i, f := range from {
|
|
if _, err := h.Write(f.Key); err != nil {
|
|
return err
|
|
}
|
|
if _, err := h.Write(f.Value); err != nil {
|
|
return err
|
|
}
|
|
|
|
tmap[h.Sum64()] = i
|
|
h.Reset()
|
|
}
|
|
|
|
s, e, d := 0, 0, 0
|
|
|
|
state := expectKey
|
|
ws, we := -1, len(b)
|
|
|
|
instr := false
|
|
slash := 0
|
|
|
|
for i := 0; i < len(b); i++ {
|
|
if instr && b[i] == '\\' {
|
|
slash++
|
|
continue
|
|
}
|
|
|
|
// skip any left padding whitespace
|
|
if ws == -1 && (b[i] == '{' || b[i] == '[') {
|
|
ws = i
|
|
}
|
|
|
|
if b[i] == '"' && (slash%2 == 0) {
|
|
instr = !instr
|
|
}
|
|
|
|
if state == expectObjClose || state == expectListClose {
|
|
if !instr {
|
|
switch b[i] {
|
|
case '{', '[':
|
|
d++
|
|
case '}', ']':
|
|
d--
|
|
}
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case state == expectKey && b[i] == '"':
|
|
state = expectKeyClose
|
|
s = i
|
|
|
|
case state == expectKeyClose && (b[i] == '"' && (slash%2 == 0)):
|
|
state = expectColon
|
|
if _, err := h.Write(b[(s + 1):i]); err != nil {
|
|
return err
|
|
}
|
|
we = s
|
|
|
|
case state == expectColon && b[i] == ':':
|
|
state = expectValue
|
|
|
|
case state == expectValue && b[i] == '"':
|
|
state = expectString
|
|
s = i
|
|
|
|
case state == expectString && (b[i] == '"' && (slash%2 == 0)):
|
|
e = i
|
|
|
|
case state == expectValue && b[i] == '[':
|
|
state = expectListClose
|
|
s = i
|
|
d++
|
|
|
|
case state == expectListClose && d == 0 && b[i] == ']':
|
|
e = i
|
|
|
|
case state == expectValue && b[i] == '{':
|
|
state = expectObjClose
|
|
s = i
|
|
d++
|
|
|
|
case state == expectObjClose && d == 0 && b[i] == '}':
|
|
e = i
|
|
|
|
case state == expectValue && (b[i] >= '0' && b[i] <= '9'):
|
|
state = expectNumClose
|
|
s = i
|
|
|
|
case state == expectNumClose &&
|
|
((b[i] < '0' || b[i] > '9') &&
|
|
(b[i] != '.' && b[i] != 'e' && b[i] != 'E' && b[i] != '+' && b[i] != '-')):
|
|
e = i - 1
|
|
|
|
case state == expectValue &&
|
|
(b[i] == 'f' || b[i] == 'F' || b[i] == 't' || b[i] == 'T'):
|
|
state = expectBoolClose
|
|
s = i
|
|
|
|
case state == expectBoolClose && (b[i] == 'e' || b[i] == 'E'):
|
|
e = i
|
|
|
|
case state == expectValue && b[i] == 'n':
|
|
state = expectNull
|
|
s = i
|
|
|
|
case state == expectNull && (b[i-1] == 'l' && b[i] == 'l'):
|
|
e = i
|
|
}
|
|
|
|
if e != 0 {
|
|
e++
|
|
|
|
if e <= s {
|
|
return errors.New("invalid json")
|
|
}
|
|
|
|
if _, err := h.Write(b[s:e]); err != nil {
|
|
return err
|
|
}
|
|
|
|
if (we + 1) <= ws {
|
|
return errors.New("invalid json")
|
|
}
|
|
|
|
n, ok := tmap[h.Sum64()]
|
|
h.Reset()
|
|
|
|
if ok {
|
|
if _, err := w.Write(b[ws:(we + 1)]); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(to[n].Key) != 0 {
|
|
var err error
|
|
|
|
if _, err := w.Write(to[n].Key); err != nil {
|
|
return err
|
|
}
|
|
if _, err := w.WriteString(`":`); err != nil {
|
|
return err
|
|
}
|
|
if len(to[n].Value) != 0 {
|
|
_, err = w.Write(to[n].Value)
|
|
} else {
|
|
_, err = w.WriteString("null")
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
ws = e
|
|
} else if b[e] == ',' {
|
|
ws = e + 1
|
|
} else {
|
|
ws = e
|
|
}
|
|
}
|
|
|
|
if !ok && (b[s] == '[' || b[s] == '{') {
|
|
// the i++ in the for loop will add 1 so we account for that (s - 1)
|
|
i = s - 1
|
|
}
|
|
|
|
state = expectKey
|
|
we = len(b)
|
|
e = 0
|
|
d = 0
|
|
}
|
|
|
|
slash = 0
|
|
}
|
|
|
|
if ws == -1 || (ws == 0 && we == len(b)) {
|
|
w.Write(b)
|
|
} else if ws < we {
|
|
w.Write(b[ws:we])
|
|
}
|
|
|
|
return nil
|
|
}
|