Skip to content

Commit f5dd6d0

Browse files
committed
Add large json file validation test
1 parent 48030e3 commit f5dd6d0

File tree

5 files changed

+304
-91
lines changed

5 files changed

+304
-91
lines changed

.github/workflows/c-cpp.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ jobs:
5858
working-directory: ./build
5959
run: ./json_parser_tests
6060

61+
- name: Run Json File Validation Test
62+
working-directory: ./build
63+
run: ./validation_test
64+
6165
- name: Generate Coverage Report
6266
run: |
6367
lcov --capture --directory ./build --output-file coverage.info \

CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ add_library(json_parser STATIC
1414
json_parser.c
1515
)
1616

17+
# Increase token count for validation test
18+
target_compile_definitions(json_parser PRIVATE JSON_DEFAULT_MAX_TOKENS=5120)
19+
1720
set_target_properties(json_parser PROPERTIES
1821
C_VISIBILITY_PRESET hidden
1922
INTERPROCEDURAL_OPTIMIZATION FALSE
@@ -54,8 +57,22 @@ target_link_libraries(json_parser_tests
5457
Threads::Threads
5558
)
5659

60+
# Validation Test executable
61+
add_executable(validation_test
62+
validation_test.cpp
63+
)
64+
65+
target_link_libraries(validation_test
66+
PRIVATE
67+
json_parser
68+
GTest::GTest
69+
GTest::Main
70+
Threads::Threads
71+
)
72+
5773
enable_testing()
5874
add_test(NAME json_parser_tests COMMAND json_parser_tests)
75+
add_test(NAME validation_test COMMAND validation_test)
5976

6077
# Installation configuration
6178
install(TARGETS json_parser

json_parser.h

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ extern "C" {
3434
#define JSON_DEFAULT_MAX_STRING 256
3535
#endif
3636

37+
// ASCII optimization for whitespace skipping
38+
#ifdef JSON_USE_SIMPLE_WHITESPACE_SKIPPING
39+
#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')
40+
#else
41+
#define IS_WHITESPACE(c) isspace(c)
42+
#endif
43+
3744
typedef enum
3845
{
3946
JSON_ERROR_NONE = 0,
@@ -46,7 +53,8 @@ typedef enum
4653
JSON_ERROR_NESTING_DEPTH,
4754
JSON_ERROR_INVALID_NUMBER,
4855
JSON_ERROR_TRAILING_CHARS,
49-
JSON_ERROR_ALLOCATION_FAILED
56+
JSON_ERROR_ALLOCATION_FAILED,
57+
JSON_ERROR_EMPTY_INPUT
5058
} json_error_t;
5159

5260
typedef enum
@@ -205,7 +213,7 @@ const json_token_t *json_get_tokens(const json_parser_t *parser, size_t *count)
205213

206214
static void json_skip_whitespace(json_parser_t *parser)
207215
{
208-
while(parser->pos < parser->length && isspace(parser->json[parser->pos]))
216+
while(parser->pos < parser->length && IS_WHITESPACE(parser->json[parser->pos]))
209217
{
210218
parser->pos++;
211219
}
@@ -592,13 +600,17 @@ static int json_parse_literal(json_parser_t *parser, const char *literal, json_t
592600
return -1;
593601
}
594602

603+
size_t literal_start_pos = parser->pos;
595604
parser->pos += len;
596605

597606
if(json_add_token(parser, type))
598607
{
599608
return -1;
600609
}
601610

611+
json_token_t *token = &parser->tokens[parser->token_count - 1];
612+
token->start = literal_start_pos;
613+
token->end = parser->pos;
602614
return 0;
603615
}
604616

@@ -674,7 +686,7 @@ static int json_parse_object(json_parser_t *parser)
674686
if(parser->json[parser->pos] == '}')
675687
{
676688
parser->pos++;
677-
obj_token->end = parser->pos; // Set end after closing '}'
689+
obj_token->end = parser->pos;
678690
parser->depth--;
679691
return 0;
680692
}
@@ -715,6 +727,8 @@ static int json_parse_object(json_parser_t *parser)
715727
return -1;
716728
}
717729

730+
json_skip_whitespace(parser);
731+
718732
if(json_parse_value(parser))
719733
{
720734
return -1;
@@ -763,7 +777,7 @@ static int json_parse_array(json_parser_t *parser)
763777
if(parser->json[parser->pos] == ']')
764778
{
765779
parser->pos++;
766-
arr_token->end = parser->pos; // End after ']'
780+
arr_token->end = parser->pos;
767781
parser->depth--;
768782
return 0;
769783
}
@@ -777,35 +791,30 @@ static int json_parse_array(json_parser_t *parser)
777791
break;
778792
}
779793

780-
// Check for closing bracket before parsing next element
781794
if(parser->json[parser->pos] == ']')
782795
{
783796
parser->pos++;
784-
arr_token->end = parser->pos; // Set end after ']'
797+
arr_token->end = parser->pos;
785798
parser->depth--;
786799
return 0;
787800
}
788801

789-
// Parse the array element
790802
if(json_parse_value(parser))
791803
{
792804
return -1;
793805
}
794806

795807
json_skip_whitespace(parser);
796808

797-
// Check for comma or closing bracket after the element
798809
if(parser->json[parser->pos] == ']')
799810
{
800-
// Handle closing bracket in the next iteration
801811
continue;
802812
}
803813
else if(parser->json[parser->pos] == ',')
804814
{
805-
parser->pos++; // Consume the comma
815+
parser->pos++;
806816
json_skip_whitespace(parser);
807817

808-
// Trailing comma check: if next character is ']', it's invalid
809818
if(parser->json[parser->pos] == ']')
810819
{
811820
json_set_error(parser, JSON_ERROR_UNEXPECTED_CHAR);
@@ -814,7 +823,6 @@ static int json_parse_array(json_parser_t *parser)
814823
}
815824
else
816825
{
817-
// Neither comma nor closing bracket
818826
json_set_error(parser, JSON_ERROR_UNEXPECTED_CHAR);
819827
return -1;
820828
}
@@ -831,7 +839,15 @@ json_error_t json_parser_parse(json_parser_t *parser)
831839

832840
if(parser->pos >= parser->length)
833841
{
834-
json_set_error(parser, JSON_ERROR_INVALID_TOKEN);
842+
if(parser->length == 0 || parser->pos == parser->length)
843+
{
844+
json_set_error(parser, JSON_ERROR_EMPTY_INPUT);
845+
}
846+
else
847+
{
848+
json_set_error(parser, JSON_ERROR_INVALID_TOKEN);
849+
}
850+
835851
return parser->error;
836852
}
837853

large_json_file.json

Lines changed: 22 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -84,99 +84,43 @@
8484
{
8585
"id": 2,
8686
"name": "Item 2",
87-
"price": 191.32,
88-
"available": true,
89-
"tags": [
90-
"tag0",
91-
"tag1",
92-
"tag2",
93-
"tag3"
94-
],
95-
"dimensions": {
96-
"length": 64.8,
97-
"width": 96.54,
98-
"height": 63.33
99-
},
100-
"ratings": [
101-
4.0,
102-
3.4,
103-
3.3,
104-
1.3,
105-
3.0,
106-
4.3,
107-
3.3
108-
],
87+
"price": 0.0,
88+
"available": false,
89+
"tags": ["tag0"],
90+
"dimensions": { "length": 0.0, "width": 0.0, "height": 0.0 },
91+
"ratings": [],
10992
"metadata": {
110-
"created_at": "2025-05-15",
93+
"created_at": "2025-01-01",
11194
"updated_at": null,
112-
"flags": {
113-
"featured": false,
114-
"discounted": false,
115-
"limited": true
116-
}
95+
"flags": { "featured": false, "discounted": false, "limited": false }
11796
}
11897
},
11998
{
12099
"id": 3,
121100
"name": "Item 3",
122-
"price": 792.26,
123-
"available": true,
124-
"tags": [
125-
"tag0",
126-
"tag1",
127-
"tag2",
128-
"tag3",
129-
"tag4"
130-
],
131-
"dimensions": {
132-
"length": 66.78,
133-
"width": 1.34,
134-
"height": 38.7
135-
},
136-
"ratings": [
137-
3.8,
138-
2.2,
139-
4.4,
140-
4.4,
141-
1.4,
142-
1.6,
143-
3.5,
144-
1.4
145-
],
101+
"price": 0.0,
102+
"available": false,
103+
"tags": ["tag0"],
104+
"dimensions": { "length": 0.0, "width": 0.0, "height": 0.0 },
105+
"ratings": [],
146106
"metadata": {
147-
"created_at": "2025-05-17",
148-
"updated_at": "2025-05-22",
149-
"flags": {
150-
"featured": false,
151-
"discounted": false,
152-
"limited": false
153-
}
107+
"created_at": "2025-01-01",
108+
"updated_at": null,
109+
"flags": { "featured": false, "discounted": false, "limited": false }
154110
}
155111
},
156112
{
157113
"id": 4,
158114
"name": "Item 4",
159-
"price": 68.15,
160-
"available": true,
161-
"tags": [
162-
"tag0"
163-
],
164-
"dimensions": {
165-
"length": 68.9,
166-
"width": 98.64,
167-
"height": 59.67
168-
},
169-
"ratings": [
170-
3.5
171-
],
115+
"price": 0.0,
116+
"available": false,
117+
"tags": ["tag0"],
118+
"dimensions": { "length": 0.0, "width": 0.0, "height": 0.0 },
119+
"ratings": [],
172120
"metadata": {
173-
"created_at": "2025-05-10",
121+
"created_at": "2025-01-01",
174122
"updated_at": null,
175-
"flags": {
176-
"featured": true,
177-
"discounted": false,
178-
"limited": false
179-
}
123+
"flags": { "featured": false, "discounted": false, "limited": false }
180124
}
181125
},
182126
{

0 commit comments

Comments
 (0)