Skip to content
Merged
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
16 changes: 11 additions & 5 deletions stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,22 @@ func (s *Stream) GetParsedLength() int {
}
}

// ensureParseNext lazily parses the next chunk of data when the token list ends mid-stream.
func (s *Stream) ensureParseNext() {
if s.current.next == nil && s.current != undefToken && s.p != nil {
n := s.p.n
s.p.parse()
s.len += s.p.n - n
}
}

// GoNext moves the stream pointer to the next token.
// If there is no token, it initiates the parsing of the next chunk of data.
// If there is no data, the pointer will point to the TokenUndef token.
func (s *Stream) GoNext() *Stream {
s.ensureParseNext()
if s.current.next != nil {
s.current = s.current.next
if s.current.next == nil && s.p != nil { // lazy load and parse next data-chunk
n := s.p.n
s.p.parse()
s.len += s.p.n - n
}
if s.historySize != 0 && s.current.id-s.head.id > s.historySize {
t := s.head
s.head = s.head.unlink()
Expand Down Expand Up @@ -262,6 +267,7 @@ func (s *Stream) PrevToken() *Token {
// If next token doesn't exist, the method returns TypeUndef token.
// Do not save a result (Token) into variables — the next token may be changed at any time.
func (s *Stream) NextToken() *Token {
s.ensureParseNext()
if s.current.next != nil {
return s.current.next
}
Expand Down
40 changes: 40 additions & 0 deletions stream_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,46 @@ func TestIssue30(t *testing.T) {
}
}

// TestIssue44 The parser stops parsing the stream after the first token if
// its size greater or equal to the size of the parser's buffer.
func TestIssue44(t *testing.T) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At fisrt, thanks for the PR. The fix itself looks correct.

One small thing: I ran TestIssue44 andTestIssue44NextToken on master and they pass there too - so they don't actually catch the bug yet. The reason is the input is exactly 43 bytes with bufferSize = 43, so preload() reads everything in one shot and the first parse() already emits both tokens before any GoNext(). The bug only shows up when the reader has more data than fits in one buffer

Could you tweak the test data so it fails without the fix? Something like bufSize=20 with "this_token_is_exactly_40_characters_long 67 next_keyword_here 99" could reproduce it - 1 token instead of 4 on master, all 4 with your fix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, this is because you added the fast path for ASCII, and ensureBytes(4) is not always called anymore. I will updates the tests and commit messages

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just made the changes and updated the commits and PR description and title

parser := New()
parser.AllowKeywordSymbols(Underscore, Numbers)

// stream with first token close to the size of the parser's internal buffer
buf := bytes.NewBufferString("this_token_is_exactly_40_characters_long 67")
stream := parser.ParseStream(buf, 40)
defer stream.Close()

require.True(t, stream.IsValid())
require.True(t, stream.CurrentToken().IsKeyword())
require.Equal(t, "this_token_is_exactly_40_characters_long", stream.CurrentToken().ValueString())

stream.GoNext()
require.True(t, stream.IsValid(), "stream should still be valid after first token")
require.True(t, stream.CurrentToken().IsInteger())
require.Equal(t, int64(67), stream.CurrentToken().ValueInt64())
}

// TestIssue44NextToken NextToken should be able to peek the next token even
// when the first token's size is grreater or equal to the size of the parser's
// buffer.
func TestIssue44NextToken(t *testing.T) {
parser := New()
parser.AllowKeywordSymbols(Underscore, Numbers)

// stream with first token close to the size of the parser's internal buffer
buf := bytes.NewBufferString("this_token_is_exactly_40_characters_long 67")
stream := parser.ParseStream(buf, 40)
defer stream.Close()

require.True(t, stream.IsValid())
next := stream.NextToken()
require.NotEqual(t, undefToken, next, "NextToken should not return undefToken")
require.True(t, next.IsInteger())
require.Equal(t, int64(67), next.ValueInt64())
}

func TestStreamOverflow(t *testing.T) {
parser := New()
buf := bytes.NewBuffer([]byte("a b c"))
Expand Down
Loading