Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,59 @@ func Test(t *testing.T) {
}
}
}

func TestLineSeparators(t *testing.T) {
tests := []struct {
name string
in string
wantErr string
}{
{
name: "U+2028 in string",
in: "\"a\u2028b\"",
},
{
name: "U+2029 in string",
in: "\"a\u2029b\"",
},
{
name: "U+2028 in block comment",
in: "/* a\u2028b */ null",
},
{
name: "U+2029 in block comment",
in: "/* a\u2029b */ null",
},
{
name: "U+2028 as whitespace",
in: "null\u2028",
wantErr: "hujson: line 1, column 5: invalid character '\\u2028' after top-level value",
},
{
name: "U+2029 as whitespace",
in: "null\u2029",
wantErr: "hujson: line 1, column 5: invalid character '\\u2029' after top-level value",
},
{
name: "U+2028 in line comment",
in: "// hidden\u2028null\ntrue",
wantErr: "hujson: line 1, column 10: invalid character '\\u2028' in line comment",
},
{
name: "U+2029 in line comment",
in: "// hidden\u2029null\ntrue",
wantErr: "hujson: line 1, column 10: invalid character '\\u2029' in line comment",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := Parse([]byte(tt.in))
switch {
case err == nil && tt.wantErr != "":
t.Fatalf("Parse() error = nil, want %q", tt.wantErr)
case err != nil && err.Error() != tt.wantErr:
t.Fatalf("Parse() error = %q, want %q", err, tt.wantErr)
}
})
}
}
15 changes: 15 additions & 0 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,19 @@ func consumeExtra(n int, b []byte) (int, error) {
n += consumeWhitespace(b[n:])
// Skip past comments.
case '/':
isLineComment := bytes.HasPrefix(b[n:], lineCommentStart)
switch nc := consumeComment(b[n:]); {
case nc == 0:
return n, nil
case nc < 0:
return n, fmt.Errorf("parsing comment: %w", io.ErrUnexpectedEOF)
case !utf8.Valid(b[n : n+nc]):
return n, fmt.Errorf("invalid UTF-8 in comment")
case isLineComment:
if i := indexLineSeparator(b[n : n+nc]); i >= 0 {
return n + i, newInvalidCharacterError(b[n+i:], "in line comment")
}
n += nc
default:
n += nc
}
Expand All @@ -245,6 +251,15 @@ func consumeWhitespace(b []byte) (n int) {
return n
}

func indexLineSeparator(b []byte) int {
i := bytes.IndexRune(b, '\u2028')
j := bytes.IndexRune(b, '\u2029')
if i < 0 || (j >= 0 && j < i) {
return j
}
return i
}

// consumeComment consumes a line or block comment start in b.
// It returns the length of the comment if valid, otherwise
// it returns 0 if it is not a comment and -1 if it is invalid.
Expand Down