♻️ change behavior for null FILE*
This commit is contained in:
parent
313e8eb0e7
commit
5a97913e93
@ -29,7 +29,7 @@ Unlike the [`parse`](parse.md) function, this function neither throws an excepti
|
|||||||
: A compatible input, for instance:
|
: A compatible input, for instance:
|
||||||
|
|
||||||
- an `std::istream` object
|
- an `std::istream` object
|
||||||
- a `FILE` pointer
|
- a `FILE` pointer (must not be null)
|
||||||
- a C-style array of characters
|
- a C-style array of characters
|
||||||
- a pointer to a null-terminated string of single byte characters
|
- a pointer to a null-terminated string of single byte characters
|
||||||
- a `std::string`
|
- a `std::string`
|
||||||
@ -64,10 +64,6 @@ Whether the input is valid JSON.
|
|||||||
|
|
||||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||||
|
|
||||||
## Exceptions
|
|
||||||
|
|
||||||
Throws [`parse_error.116`](../../home/exceptions.md#jsonexceptionparse_error116) if passed `#!cpp FILE` pointer is `#!cpp nullptr`.
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the length of the input. The parser is a predictive LL(1) parser.
|
Linear in the length of the input. The parser is a predictive LL(1) parser.
|
||||||
@ -76,6 +72,11 @@ Linear in the length of the input. The parser is a predictive LL(1) parser.
|
|||||||
|
|
||||||
(1) A UTF-8 byte order mark is silently ignored.
|
(1) A UTF-8 byte order mark is silently ignored.
|
||||||
|
|
||||||
|
!!! danger "Runtime assertion"
|
||||||
|
|
||||||
|
The precondition that a passed `#!cpp FILE` pointer must not be null is enforced with a
|
||||||
|
[runtime assertion](../../features/assertions.md).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|||||||
@ -28,7 +28,7 @@ static basic_json parse(IteratorType first, IteratorType last,
|
|||||||
: A compatible input, for instance:
|
: A compatible input, for instance:
|
||||||
|
|
||||||
- an `std::istream` object
|
- an `std::istream` object
|
||||||
- a `FILE` pointer
|
- a `FILE` pointer (must not be null)
|
||||||
- a C-style array of characters
|
- a C-style array of characters
|
||||||
- a pointer to a null-terminated string of single byte characters
|
- a pointer to a null-terminated string of single byte characters
|
||||||
- a `std::string`
|
- a `std::string`
|
||||||
@ -77,7 +77,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
- Throws [`parse_error.102`](../../home/exceptions.md#jsonexceptionparse_error102) if to_unicode fails or surrogate
|
- Throws [`parse_error.102`](../../home/exceptions.md#jsonexceptionparse_error102) if to_unicode fails or surrogate
|
||||||
error.
|
error.
|
||||||
- Throws [`parse_error.103`](../../home/exceptions.md#jsonexceptionparse_error103) if to_unicode fails.
|
- Throws [`parse_error.103`](../../home/exceptions.md#jsonexceptionparse_error103) if to_unicode fails.
|
||||||
- Throws [`parse_error.116`](../../home/exceptions.md#jsonexceptionparse_error116) if passed `#!cpp FILE` pointer is `#!cpp nullptr`.
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@ -89,6 +88,11 @@ super-linear complexity.
|
|||||||
|
|
||||||
(1) A UTF-8 byte order mark is silently ignored.
|
(1) A UTF-8 byte order mark is silently ignored.
|
||||||
|
|
||||||
|
!!! danger "Runtime assertion"
|
||||||
|
|
||||||
|
The precondition that a passed `#!cpp FILE` pointer must not be null is enforced with a
|
||||||
|
[runtime assertion](../../features/assertions.md).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example "Parsing from a character array"
|
??? example "Parsing from a character array"
|
||||||
|
|||||||
@ -102,3 +102,30 @@ behavior and yields a runtime assertion.
|
|||||||
```
|
```
|
||||||
Assertion failed: (m_object != nullptr), function operator++, file iter_impl.hpp, line 368.
|
Assertion failed: (m_object != nullptr), function operator++, file iter_impl.hpp, line 368.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Reading from a null `FILE` pointer
|
||||||
|
|
||||||
|
Reading from a null `#!cpp FILE` pointer is undefined behavior and yields a runtime assertion. This can happen when
|
||||||
|
calling `#!cpp std::fopen` on a nonexisting file.
|
||||||
|
|
||||||
|
??? example "Example 4: Uninitialized iterator"
|
||||||
|
|
||||||
|
The following code will trigger an assertion at runtime:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::FILE* f = std::fopen("nonexisting_file.json", "r");
|
||||||
|
json j = json::parse(f);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```
|
||||||
|
Assertion failed: (m_file != nullptr), function file_input_adapter, file input_adapters.hpp, line 55.
|
||||||
|
```
|
||||||
|
|||||||
@ -354,16 +354,6 @@ A UBJSON high-precision number could not be parsed.
|
|||||||
[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A
|
[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A
|
||||||
```
|
```
|
||||||
|
|
||||||
### json.exception.parse_error.116
|
|
||||||
|
|
||||||
A `#!cpp FILE` pointer passed to the [parse](../api/basic_json/parse.md) function is `#!cpp nullptr`; that is, a previous call to `#!cpp std::fopen` failed.
|
|
||||||
|
|
||||||
!!! failure "Example message"
|
|
||||||
|
|
||||||
```
|
|
||||||
[json.exception.parse_error.116] parse error: input file is invalid
|
|
||||||
```
|
|
||||||
|
|
||||||
## Iterator errors
|
## Iterator errors
|
||||||
|
|
||||||
This exception is thrown if iterators passed to a library function do not match
|
This exception is thrown if iterators passed to a library function do not match
|
||||||
|
|||||||
@ -49,13 +49,10 @@ class file_input_adapter
|
|||||||
using char_type = char;
|
using char_type = char;
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
explicit file_input_adapter(std::FILE* f)
|
explicit file_input_adapter(std::FILE* f) noexcept
|
||||||
: m_file(f)
|
: m_file(f)
|
||||||
{
|
{
|
||||||
if (m_file == nullptr)
|
JSON_ASSERT(m_file != nullptr);
|
||||||
{
|
|
||||||
JSON_THROW(parse_error::create(116, 0, "input file is invalid", nullptr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make class move-only
|
// make class move-only
|
||||||
@ -67,7 +64,11 @@ class file_input_adapter
|
|||||||
|
|
||||||
std::char_traits<char>::int_type get_character() noexcept
|
std::char_traits<char>::int_type get_character() noexcept
|
||||||
{
|
{
|
||||||
return std::fgetc(m_file);
|
if (JSON_HEDLEY_LIKELY(m_file != nullptr))
|
||||||
|
{
|
||||||
|
return std::fgetc(m_file);
|
||||||
|
}
|
||||||
|
return std::char_traits<char>::eof(); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -5936,13 +5936,10 @@ class file_input_adapter
|
|||||||
using char_type = char;
|
using char_type = char;
|
||||||
|
|
||||||
JSON_HEDLEY_NON_NULL(2)
|
JSON_HEDLEY_NON_NULL(2)
|
||||||
explicit file_input_adapter(std::FILE* f)
|
explicit file_input_adapter(std::FILE* f) noexcept
|
||||||
: m_file(f)
|
: m_file(f)
|
||||||
{
|
{
|
||||||
if (m_file == nullptr)
|
JSON_ASSERT(m_file != nullptr);
|
||||||
{
|
|
||||||
JSON_THROW(parse_error::create(116, 0, "input file is invalid", nullptr));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// make class move-only
|
// make class move-only
|
||||||
@ -5954,7 +5951,11 @@ class file_input_adapter
|
|||||||
|
|
||||||
std::char_traits<char>::int_type get_character() noexcept
|
std::char_traits<char>::int_type get_character() noexcept
|
||||||
{
|
{
|
||||||
return std::fgetc(m_file);
|
if (JSON_HEDLEY_LIKELY(m_file != nullptr))
|
||||||
|
{
|
||||||
|
return std::fgetc(m_file);
|
||||||
|
}
|
||||||
|
return std::char_traits<char>::eof(); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -166,26 +166,6 @@ struct SaxEventLoggerExitAfterStartArray : public SaxEventLogger
|
|||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// Passing a NULL pointer to the input adapter violates its NON_NULL attribute which is detected by UBSAN.
|
|
||||||
// To still test whether exceptions are thrown, we need to exclude these tests from UBSAN which can only
|
|
||||||
// be done with a function attribute. See
|
|
||||||
// https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#disabling-instrumentation-with-attribute-no-sanitize-undefined
|
|
||||||
#if defined(__clang__)
|
|
||||||
__attribute__((no_sanitize("undefined")))
|
|
||||||
#endif
|
|
||||||
void test_file_exception();
|
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
__attribute__((no_sanitize("undefined")))
|
|
||||||
#endif
|
|
||||||
void test_file_exception()
|
|
||||||
{
|
|
||||||
std::FILE* f = std::fopen("nonexisting_file", "r"); // NOLINT(cppcoreguidelines-owning-memory)
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::parse(f), "[json.exception.parse_error.116] parse error: input file is invalid", json::parse_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json::accept(f), "[json.exception.parse_error.116] parse error: input file is invalid", json::parse_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("deserialization")
|
TEST_CASE("deserialization")
|
||||||
{
|
{
|
||||||
SECTION("successful deserialization")
|
SECTION("successful deserialization")
|
||||||
@ -352,11 +332,6 @@ TEST_CASE("deserialization")
|
|||||||
{
|
{
|
||||||
CHECK_THROWS_WITH_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&);
|
CHECK_THROWS_WITH_AS("[\"foo\",1,2,3,false,{\"one\":1}"_json, "[json.exception.parse_error.101] parse error at line 1, column 29: syntax error while parsing array - unexpected end of input; expected ']'", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("FILE*")
|
|
||||||
{
|
|
||||||
test_file_exception();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("contiguous containers")
|
SECTION("contiguous containers")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user