mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-02 13:20:14 +00:00
refactor(jsoncommentstrip): replace go-jsoncommentstrip with custom JSON comment stripping
This commit is contained in:
parent
d79b812467
commit
8939f31d55
3 changed files with 293 additions and 1 deletions
128
utils/jsoncommentstrip/jsoncommentstrip.go
Normal file
128
utils/jsoncommentstrip/jsoncommentstrip.go
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Package jsoncommentstrip provides an io.Reader that strips JavaScript-style
|
||||
// comments (// line and /* block */) from JSON input while preserving
|
||||
// comment-like sequences inside JSON string values.
|
||||
package jsoncommentstrip
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
type state int
|
||||
|
||||
const (
|
||||
stateNormal state = iota
|
||||
stateInString
|
||||
stateInStringEscape
|
||||
stateMaybeComment // saw '/'
|
||||
stateLineComment // inside // ...
|
||||
stateBlockComment // inside /* ... */
|
||||
stateMaybeBlockEnd // saw '*' inside block comment
|
||||
)
|
||||
|
||||
type reader struct {
|
||||
r *bufio.Reader
|
||||
state state
|
||||
}
|
||||
|
||||
// NewReader returns an io.Reader that strips JSON comments from the
|
||||
// underlying reader. It removes single-line comments (// to end of line)
|
||||
// and block comments (/* ... */), while preserving comment-like sequences
|
||||
// that appear inside JSON string values.
|
||||
func NewReader(r io.Reader) io.Reader {
|
||||
return &reader{
|
||||
r: bufio.NewReader(r),
|
||||
state: stateNormal,
|
||||
}
|
||||
}
|
||||
|
||||
func (cr *reader) Read(p []byte) (int, error) {
|
||||
n := 0
|
||||
for n < len(p) {
|
||||
b, err := cr.r.ReadByte()
|
||||
if err != nil {
|
||||
if cr.state == stateMaybeComment {
|
||||
// Emit the pending '/' before returning EOF
|
||||
p[n] = '/'
|
||||
n++
|
||||
cr.state = stateNormal
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
switch cr.state {
|
||||
case stateNormal:
|
||||
switch b {
|
||||
case '"':
|
||||
p[n] = b
|
||||
n++
|
||||
cr.state = stateInString
|
||||
case '/':
|
||||
cr.state = stateMaybeComment
|
||||
default:
|
||||
p[n] = b
|
||||
n++
|
||||
}
|
||||
|
||||
case stateInString:
|
||||
p[n] = b
|
||||
n++
|
||||
switch b {
|
||||
case '\\':
|
||||
cr.state = stateInStringEscape
|
||||
case '"':
|
||||
cr.state = stateNormal
|
||||
}
|
||||
|
||||
case stateInStringEscape:
|
||||
p[n] = b
|
||||
n++
|
||||
cr.state = stateInString
|
||||
|
||||
case stateMaybeComment:
|
||||
switch b {
|
||||
case '/':
|
||||
cr.state = stateLineComment
|
||||
case '*':
|
||||
cr.state = stateBlockComment
|
||||
default:
|
||||
// The '/' was not a comment start; emit it and the current byte
|
||||
p[n] = '/'
|
||||
n++
|
||||
if n < len(p) {
|
||||
p[n] = b
|
||||
n++
|
||||
} else {
|
||||
// We need to "unread" the current byte since buffer is full
|
||||
_ = cr.r.UnreadByte()
|
||||
}
|
||||
cr.state = stateNormal
|
||||
}
|
||||
|
||||
case stateLineComment:
|
||||
if b == '\n' || b == '\r' {
|
||||
p[n] = b
|
||||
n++
|
||||
cr.state = stateNormal
|
||||
}
|
||||
// Otherwise, consume and discard
|
||||
|
||||
case stateBlockComment:
|
||||
if b == '*' {
|
||||
cr.state = stateMaybeBlockEnd
|
||||
}
|
||||
// Otherwise, consume and discard
|
||||
|
||||
case stateMaybeBlockEnd:
|
||||
if b == '/' {
|
||||
cr.state = stateNormal
|
||||
} else if b == '*' {
|
||||
// Stay in stateMaybeBlockEnd (consecutive *'s)
|
||||
cr.state = stateMaybeBlockEnd
|
||||
} else {
|
||||
cr.state = stateBlockComment
|
||||
}
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue