Skip to content

Commit 6d1b347

Browse files
committed
Expose getLocationLineAndColumn() as a public API
Allows better use of the offset data returned by getStructuredErrors()
1 parent 42e892d commit 6d1b347

File tree

3 files changed

+73
-20
lines changed

3 files changed

+73
-20
lines changed

include/json/reader.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,15 @@ bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
395395
*/
396396
JSON_API IStream& operator>>(IStream&, Value&);
397397

398+
struct LineAndColumn {
399+
int line;
400+
int column;
401+
};
402+
403+
/** Get the line and column of a character within a string.
404+
*/
405+
LineAndColumn JSON_API getLocationLineAndColumn(char const* beginDoc, char const* endDoc,
406+
size_t location);
398407
} // namespace Json
399408

400409
#pragma pack(pop)

src/lib_json/json_reader.cpp

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ bool Reader::decodeDouble(Token& token, Value& decoded) {
608608
value = -std::numeric_limits<double>::infinity();
609609
else if (!std::isinf(value))
610610
return addError(
611-
"'" + String(token.start_, token.end_) + "' is not a number.", token);
611+
"'" + String(token.start_, token.end_) + "' is not a number.", token);
612612
}
613613
decoded = value;
614614
return true;
@@ -767,24 +767,11 @@ Reader::Char Reader::getNextChar() {
767767

768768
void Reader::getLocationLineAndColumn(Location location, int& line,
769769
int& column) const {
770-
Location current = begin_;
771-
Location lastLineStart = current;
772-
line = 0;
773-
while (current < location && current != end_) {
774-
Char c = *current++;
775-
if (c == '\r') {
776-
if (*current == '\n')
777-
++current;
778-
lastLineStart = current;
779-
++line;
780-
} else if (c == '\n') {
781-
lastLineStart = current;
782-
++line;
783-
}
784-
}
785-
// column & line start at 1
786-
column = int(location - lastLineStart) + 1;
787-
++line;
770+
Json::LineAndColumn lineAndColumn = Json::getLocationLineAndColumn(
771+
document_.data(), document_.data() + document_.length(),
772+
location - document_.data());
773+
line = lineAndColumn.line;
774+
column = lineAndColumn.column;
788775
}
789776

790777
String Reader::getLocationLineAndColumn(Location location) const {
@@ -1660,7 +1647,7 @@ bool OurReader::decodeDouble(Token& token, Value& decoded) {
16601647
value = -std::numeric_limits<double>::infinity();
16611648
else if (!std::isinf(value))
16621649
return addError(
1663-
"'" + String(token.start_, token.end_) + "' is not a number.", token);
1650+
"'" + String(token.start_, token.end_) + "' is not a number.", token);
16641651
}
16651652
decoded = value;
16661653
return true;
@@ -1991,6 +1978,32 @@ bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
19911978
return reader->parse(begin, end, root, errs);
19921979
}
19931980

1981+
Json::LineAndColumn getLocationLineAndColumn(char const* beginDoc,
1982+
char const* endDoc,
1983+
size_t location) {
1984+
Json::LineAndColumn lineAndColumn;
1985+
char const* current = beginDoc;
1986+
char const* lastLineStart = current;
1987+
int line = 0;
1988+
while (current < beginDoc + location && current < endDoc) {
1989+
char c = *current++;
1990+
if (c == '\r') {
1991+
if (*current == '\n')
1992+
++current;
1993+
lastLineStart = current;
1994+
++line;
1995+
} else if (c == '\n') {
1996+
lastLineStart = current;
1997+
++line;
1998+
}
1999+
}
2000+
// column & line start at 1
2001+
lineAndColumn.column = int(beginDoc + location - lastLineStart) + 1;
2002+
lineAndColumn.line = ++line;
2003+
2004+
return lineAndColumn;
2005+
}
2006+
19942007
IStream& operator>>(IStream& sin, Value& root) {
19952008
CharReaderBuilder b;
19962009
String errs;

src/test_lib_json/main.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3903,6 +3903,37 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
39033903
example.size()));
39043904
}
39053905

3906+
struct GetLocationLineAndColumnTest : JsonTest::TestCase {
3907+
void testLineAndColumn(const std::string& doc, ptrdiff_t location, int row,
3908+
int column) {
3909+
Json::LineAndColumn actualLocation = Json::getLocationLineAndColumn(
3910+
doc.data(), doc.data() + doc.length(), location);
3911+
JSONTEST_ASSERT_EQUAL(row, actualLocation.line);
3912+
JSONTEST_ASSERT_EQUAL(column, actualLocation.column);
3913+
}
3914+
};
3915+
3916+
JSONTEST_FIXTURE_LOCAL(GetLocationLineAndColumnTest, test) {
3917+
const std::string example = "line 1\nline 2\r\nline 3\rline 4";
3918+
size_t found_l = -1;
3919+
size_t found_i = -1;
3920+
size_t found_n = -1;
3921+
size_t found_e = -1;
3922+
size_t found_space = -1;
3923+
for (int row = 1; row <= 4; ++row) {
3924+
found_l = example.find('l', found_l + 1);
3925+
found_i = example.find('i', found_i + 1);
3926+
found_n = example.find('n', found_n + 1);
3927+
found_e = example.find('e', found_e + 1);
3928+
found_space = example.find(' ', found_space + 1);
3929+
testLineAndColumn(example, found_l, row, 1);
3930+
testLineAndColumn(example, found_i, row, 2);
3931+
testLineAndColumn(example, found_n, row, 3);
3932+
testLineAndColumn(example, found_e, row, 4);
3933+
testLineAndColumn(example, found_space, row, 5);
3934+
}
3935+
}
3936+
39063937
int main(int argc, const char* argv[]) {
39073938
JsonTest::Runner runner;
39083939

0 commit comments

Comments
 (0)