1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-11-04 09:17:32 -05:00
v2fly/infra/conf/json/reader.go
Huang-Huang Bao f0236233f3 Fix: json.Reader: fill output bytes as much as possible
Fix JSON parsing state machine so Read([]byte) can accept byte array of any length.

This fixes a infinite read with io.ReadFull().
2022-09-07 10:50:17 +01:00

150 lines
2.8 KiB
Go

package json
import (
"io"
"github.com/v2fly/v2ray-core/v5/common/buf"
)
// State is the internal state of parser.
type State byte
const (
StateContent State = iota
StateEscape
StateDoubleQuote
StateDoubleQuoteEscape
StateSingleQuote
StateSingleQuoteEscape
StateComment
StateSlash
StateMultilineComment
StateMultilineCommentStar
)
// Reader is a reader for filtering comments.
// It supports Java style single and multi line comment syntax, and Python style single line comment syntax.
type Reader struct {
io.Reader
state State
pending []byte
br *buf.BufferedReader
}
// Read implements io.Reader.Read().
func (v *Reader) Read(b []byte) (int, error) {
if v.br == nil {
v.br = &buf.BufferedReader{Reader: buf.NewReader(v.Reader)}
}
p := b[:0]
for len(p) < len(b) {
if len(v.pending) > 0 {
max := len(b) - len(p)
if max > len(v.pending) {
max = len(v.pending)
}
p = append(p, v.pending[:max]...)
v.pending = v.pending[max:]
continue
}
x, err := v.br.ReadByte()
if err != nil {
if len(p) == 0 {
return 0, err
}
return len(p), nil
}
switch v.state {
case StateContent:
switch x {
case '"':
v.state = StateDoubleQuote
p = append(p, x)
case '\'':
v.state = StateSingleQuote
p = append(p, x)
case '\\':
v.state = StateEscape
p = append(p, x)
case '#':
v.state = StateComment
case '/':
v.state = StateSlash
default:
p = append(p, x)
}
case StateEscape:
p = append(p, x)
v.state = StateContent
case StateDoubleQuote:
switch x {
case '"':
v.state = StateContent
p = append(p, x)
case '\\':
v.state = StateDoubleQuoteEscape
p = append(p, x)
default:
p = append(p, x)
}
case StateDoubleQuoteEscape:
p = append(p, x)
v.state = StateDoubleQuote
case StateSingleQuote:
switch x {
case '\'':
v.state = StateContent
p = append(p, x)
case '\\':
v.state = StateSingleQuoteEscape
p = append(p, x)
default:
p = append(p, x)
}
case StateSingleQuoteEscape:
p = append(p, x)
v.state = StateSingleQuote
case StateComment:
if x == '\n' {
v.state = StateContent
p = append(p, x)
}
case StateSlash:
switch x {
case '/':
v.state = StateComment
case '*':
v.state = StateMultilineComment
default:
v.state = StateContent
v.pending = append(v.pending, x)
p = append(p, '/')
}
case StateMultilineComment:
switch x {
case '*':
v.state = StateMultilineCommentStar
case '\n':
p = append(p, x)
}
case StateMultilineCommentStar:
switch x {
case '/':
v.state = StateContent
case '*':
// Stay
case '\n':
p = append(p, x)
default:
v.state = StateMultilineComment
}
default:
panic("Unknown state.")
}
}
return len(p), nil
}