Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/ds/IncrementalTokenizer.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/IncrementalTokenizer.h"
8
9
#include "mozilla/AutoRestore.h"
10
11
#include "nsIInputStream.h"
12
#include "IncrementalTokenizer.h"
13
#include <algorithm>
14
15
namespace mozilla {
16
17
IncrementalTokenizer::IncrementalTokenizer(Consumer&& aConsumer,
18
                                           const char * aWhitespaces,
19
                                           const char * aAdditionalWordChars,
20
                                           uint32_t aRawMinBuffered)
21
  : TokenizerBase(aWhitespaces, aAdditionalWordChars)
22
#ifdef DEBUG
23
  , mConsuming(false)
24
#endif
25
  , mNeedMoreInput(false)
26
  , mRollback(false)
27
  , mInputCursor(0)
28
  , mConsumer(std::move(aConsumer))
29
0
{
30
0
  mInputFinished = false;
31
0
  mMinRawDelivery = aRawMinBuffered;
32
0
}
33
34
nsresult IncrementalTokenizer::FeedInput(const nsACString & aInput)
35
0
{
36
0
  NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
37
0
  MOZ_ASSERT(!mInputFinished);
38
0
39
0
  mInput.Cut(0, mInputCursor);
40
0
  mInputCursor = 0;
41
0
42
0
  mInput.Append(aInput);
43
0
44
0
  return Process();
45
0
}
46
47
nsresult IncrementalTokenizer::FeedInput(nsIInputStream * aInput, uint32_t aCount)
48
0
{
49
0
  NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
50
0
  MOZ_ASSERT(!mInputFinished);
51
0
  MOZ_ASSERT(!mConsuming);
52
0
53
0
  mInput.Cut(0, mInputCursor);
54
0
  mInputCursor = 0;
55
0
56
0
  nsresult rv = NS_OK;
57
0
  while (NS_SUCCEEDED(rv) && aCount) {
58
0
    nsCString::index_type remainder = mInput.Length();
59
0
    nsCString::index_type load =
60
0
      std::min<nsCString::index_type>(aCount, PR_UINT32_MAX - remainder);
61
0
62
0
    if (!load) {
63
0
      // To keep the API simple, we fail if the input data buffer if filled.
64
0
      // It's highly unlikely there will ever be such amout of data cumulated
65
0
      // unless a logic fault in the consumer code.
66
0
      NS_ERROR("IncrementalTokenizer consumer not reading data?");
67
0
      return NS_ERROR_OUT_OF_MEMORY;
68
0
    }
69
0
70
0
    if (!mInput.SetLength(remainder + load, fallible)) {
71
0
      return NS_ERROR_OUT_OF_MEMORY;
72
0
    }
73
0
74
0
    auto buffer = mInput.BeginWriting() + remainder;
75
0
76
0
    uint32_t read;
77
0
    rv = aInput->Read(buffer, load, &read);
78
0
    if (NS_SUCCEEDED(rv)) {
79
0
      // remainder + load fits the uint32_t size, so must remainder + read.
80
0
      mInput.SetLength(remainder + read);
81
0
      aCount -= read;
82
0
83
0
      rv = Process();
84
0
    }
85
0
  }
86
0
87
0
  return rv;
88
0
}
89
90
nsresult IncrementalTokenizer::FinishInput()
91
0
{
92
0
  NS_ENSURE_TRUE(mConsumer, NS_ERROR_NOT_INITIALIZED);
93
0
  MOZ_ASSERT(!mInputFinished);
94
0
  MOZ_ASSERT(!mConsuming);
95
0
96
0
  mInput.Cut(0, mInputCursor);
97
0
  mInputCursor = 0;
98
0
99
0
  mInputFinished = true;
100
0
  nsresult rv = Process();
101
0
  mConsumer = nullptr;
102
0
  return rv;
103
0
}
104
105
bool IncrementalTokenizer::Next(Token & aToken)
106
0
{
107
0
  // Assert we are called only from the consumer callback
108
0
  MOZ_ASSERT(mConsuming);
109
0
110
0
  if (mPastEof) {
111
0
    return false;
112
0
  }
113
0
114
0
  nsACString::const_char_iterator next = Parse(aToken);
115
0
  mPastEof = aToken.Type() == TOKEN_EOF;
116
0
  if (next == mCursor && !mPastEof) {
117
0
    // Not enough input to make a deterministic decision.
118
0
    return false;
119
0
  }
120
0
121
0
  AssignFragment(aToken, mCursor, next);
122
0
  mCursor = next;
123
0
  return true;
124
0
}
125
126
void IncrementalTokenizer::NeedMoreInput()
127
0
{
128
0
  // Assert we are called only from the consumer callback
129
0
  MOZ_ASSERT(mConsuming);
130
0
131
0
  // When the input has been finished, we can't set the flag to prevent
132
0
  // indefinite wait for more input (that will never come)
133
0
  mNeedMoreInput = !mInputFinished;
134
0
}
135
136
void IncrementalTokenizer::Rollback()
137
0
{
138
0
  // Assert we are called only from the consumer callback
139
0
  MOZ_ASSERT(mConsuming);
140
0
141
0
  mRollback = true;
142
0
}
143
144
nsresult IncrementalTokenizer::Process()
145
0
{
146
#ifdef DEBUG
147
  // Assert we are not re-entered
148
  MOZ_ASSERT(!mConsuming);
149
150
  AutoRestore<bool> consuming(mConsuming);
151
  mConsuming = true;
152
#endif
153
154
0
  MOZ_ASSERT(!mPastEof);
155
0
156
0
  nsresult rv = NS_OK;
157
0
158
0
  mInput.BeginReading(mCursor);
159
0
  mCursor += mInputCursor;
160
0
  mInput.EndReading(mEnd);
161
0
162
0
  while (NS_SUCCEEDED(rv) && !mPastEof) {
163
0
    Token token;
164
0
    nsACString::const_char_iterator next = Parse(token);
165
0
    mPastEof = token.Type() == TOKEN_EOF;
166
0
    if (next == mCursor && !mPastEof) {
167
0
      // Not enough input to make a deterministic decision.
168
0
      break;
169
0
    }
170
0
171
0
    AssignFragment(token, mCursor, next);
172
0
173
0
    nsACString::const_char_iterator rollback = mCursor;
174
0
    mCursor = next;
175
0
176
0
    mNeedMoreInput = mRollback = false;
177
0
178
0
    rv = mConsumer(token, *this);
179
0
    if (NS_FAILED(rv)) {
180
0
      break;
181
0
    }
182
0
    if (mNeedMoreInput || mRollback) {
183
0
      mCursor = rollback;
184
0
      mPastEof = false;
185
0
      if (mNeedMoreInput) {
186
0
        break;
187
0
      }
188
0
    }
189
0
  }
190
0
191
0
  mInputCursor = mCursor - mInput.BeginReading();
192
0
  return rv;
193
0
}
194
195
} // mozilla