Skip to content

Commit 459332d

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

File tree

4 files changed

+69
-24
lines changed

4 files changed

+69
-24
lines changed

include/json/reader.h

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

398+
/** Line and column within a document (1-based). */
399+
struct DocumentLocation {
400+
int line;
401+
int column;
402+
};
403+
404+
/** Return the location of a character within a string. */
405+
DocumentLocation JSON_API locateInDocument(char const* beginDoc, size_t offset);
398406
} // namespace Json
399407

400408
#pragma pack(pop)

include/json/value.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,23 +437,23 @@ class JSON_API Value {
437437
/// \post type() is arrayValue
438438
void resize(ArrayIndex newSize);
439439

440-
//@{
440+
///@{
441441
/// Access an array element (zero based index). If the array contains less
442442
/// than index element, then null value are inserted in the array so that
443443
/// its size is index+1.
444444
/// (You may need to say 'value[0u]' to get your compiler to distinguish
445445
/// this from the operator[] which takes a string.)
446446
Value& operator[](ArrayIndex index);
447447
Value& operator[](int index);
448-
//@}
448+
///@}
449449

450-
//@{
450+
///@{
451451
/// Access an array element (zero based index).
452452
/// (You may need to say 'value[0u]' to get your compiler to distinguish
453453
/// this from the operator[] which takes a string.)
454454
const Value& operator[](ArrayIndex index) const;
455455
const Value& operator[](int index) const;
456-
//@}
456+
///@}
457457

458458
/// If the array contains at least index+1 elements, returns the element
459459
/// value, otherwise returns defaultValue.

src/lib_json/json_reader.cpp

Lines changed: 27 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,9 @@ 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+
auto loc = locateInDocument(document_.data(), location - document_.data());
771+
line = loc.line;
772+
column = loc.column;
788773
}
789774

790775
String Reader::getLocationLineAndColumn(Location location) const {
@@ -1660,7 +1645,7 @@ bool OurReader::decodeDouble(Token& token, Value& decoded) {
16601645
value = -std::numeric_limits<double>::infinity();
16611646
else if (!std::isinf(value))
16621647
return addError(
1663-
"'" + String(token.start_, token.end_) + "' is not a number.", token);
1648+
"'" + String(token.start_, token.end_) + "' is not a number.", token);
16641649
}
16651650
decoded = value;
16661651
return true;
@@ -1991,6 +1976,28 @@ bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
19911976
return reader->parse(begin, end, root, errs);
19921977
}
19931978

1979+
DocumentLocation locateInDocument(char const* beginDoc, size_t offset) {
1980+
int line = 1;
1981+
int col = 1;
1982+
const char* last = beginDoc + offset;
1983+
for (; beginDoc != last; ++beginDoc) {
1984+
switch (*beginDoc) {
1985+
case '\r':
1986+
if (beginDoc + 1 != last && beginDoc[1] == '\n')
1987+
continue; // consume CRLF as a single token.
1988+
[[fallthrough]]; // CR without a following LF is same as LF
1989+
case '\n':
1990+
col = 1;
1991+
++line;
1992+
break;
1993+
default:
1994+
++col;
1995+
break;
1996+
}
1997+
}
1998+
return {line, col};
1999+
}
2000+
19942001
IStream& operator>>(IStream& sin, Value& root) {
19952002
CharReaderBuilder b;
19962003
String errs;

src/test_lib_json/main.cpp

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

3906+
struct LocateInDocumentTest : JsonTest::TestCase {
3907+
void testLocateInDocument(const char* doc, size_t location, int row,
3908+
int column) {
3909+
Json::DocumentLocation actualLocation =
3910+
Json::locateInDocument(doc, location);
3911+
JSONTEST_ASSERT_EQUAL(row, actualLocation.line);
3912+
JSONTEST_ASSERT_EQUAL(column, actualLocation.column);
3913+
}
3914+
};
3915+
3916+
JSONTEST_FIXTURE_LOCAL(LocateInDocumentTest, locateInDocTest) {
3917+
const std::string example1 = "line 1\nline 2\r\nline 3\rline 4";
3918+
const char* example2 = "\nline 2\r\r\n\nline 5\0 \n\rline 7\r\n";
3919+
struct TestSpec {
3920+
const char* doc;
3921+
size_t offset;
3922+
int line;
3923+
int column;
3924+
};
3925+
const TestSpec specs[] = {
3926+
{example1.data(), example1.find("line 1"), 1, 1},
3927+
{example1.data(), example1.find("e 4"), 4, 4},
3928+
// string terminates at \0, can't use find()
3929+
{example2, 21, 7, 1},
3930+
};
3931+
for (const auto& spec : specs) {
3932+
testLocateInDocument(spec.doc, spec.offset, spec.line, spec.column);
3933+
}
3934+
}
3935+
39063936
int main(int argc, const char* argv[]) {
39073937
JsonTest::Runner runner;
39083938

0 commit comments

Comments
 (0)