Commit 761de0f5 authored by Emmanuele Bassi's avatar Emmanuele Bassi 👣

Merge branch 'memory-mapping' into 'master'

json-parser: Support loading files via memory mapping

See merge request !27
parents 0a562b86 dd7a7112
Pipeline #188826 passed with stage
in 6 minutes and 27 seconds
......@@ -183,6 +183,7 @@ JsonParserClass
json_parser_new
json_parser_new_immutable
json_parser_load_from_file
json_parser_load_from_mapped_file
json_parser_load_from_data
json_parser_load_from_stream
json_parser_load_from_stream_async
......
......@@ -1089,6 +1089,9 @@ json_parser_load (JsonParser *parser,
* Loads a JSON stream from the content of @filename and parses it. See
* json_parser_load_from_data().
*
* If the file is large or shared between processes,
* json_parser_load_from_mapped_file() may be a more efficient way to load it.
*
* Return value: %TRUE if the file was successfully loaded and parsed.
* In case of error, @error is set accordingly and %FALSE is returned
*/
......@@ -1131,6 +1134,61 @@ json_parser_load_from_file (JsonParser *parser,
return retval;
}
/**
* json_parser_load_from_mapped_file:
* @parser: a #JsonParser
* @filename: the path for the file to parse
* @error: return location for a #GError, or %NULL
*
* Loads a JSON stream from the content of @filename and parses it. Unlike
* json_parser_load_from_file(), @filename will be memory mapped as read-only
* and parsed. @filename will be unmapped before this function returns.
*
* If mapping or reading the file fails, a %G_FILE_ERROR will be returned.
*
* Return value: %TRUE if the file was successfully loaded and parsed.
* In case of error, @error is set accordingly and %FALSE is returned
* Since: 1.6
*/
gboolean
json_parser_load_from_mapped_file (JsonParser *parser,
const gchar *filename,
GError **error)
{
JsonParserPrivate *priv;
GError *internal_error = NULL;
gboolean retval = TRUE;
GMappedFile *mapped_file = NULL;
g_return_val_if_fail (JSON_IS_PARSER (parser), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
priv = parser->priv;
mapped_file = g_mapped_file_new (filename, FALSE, &internal_error);
if (mapped_file == NULL)
{
g_propagate_error (error, internal_error);
return FALSE;
}
g_free (priv->filename);
priv->is_filename = TRUE;
priv->filename = g_strdup (filename);
if (!json_parser_load (parser, g_mapped_file_get_contents (mapped_file),
g_mapped_file_get_length (mapped_file), &internal_error))
{
g_propagate_error (error, internal_error);
retval = FALSE;
}
g_clear_pointer (&mapped_file, g_mapped_file_unref);
return retval;
}
/**
* json_parser_load_from_data:
* @parser: a #JsonParser
......
......@@ -153,6 +153,10 @@ JSON_AVAILABLE_IN_1_0
gboolean json_parser_load_from_file (JsonParser *parser,
const gchar *filename,
GError **error);
JSON_AVAILABLE_IN_1_6
gboolean json_parser_load_from_mapped_file (JsonParser *parser,
const gchar *filename,
GError **error);
JSON_AVAILABLE_IN_1_0
gboolean json_parser_load_from_data (JsonParser *parser,
const gchar *data,
......
......@@ -16,6 +16,7 @@ tests = [
]
test_data = [
'invalid.json',
'stream-load.json',
]
......
......@@ -689,20 +689,13 @@ test_stream_sync (void)
g_free (path);
}
/* Assert that the JSON in @parser was correctly loaded from stream-load.json. */
static void
on_load_complete (GObject *gobject,
GAsyncResult *result,
gpointer user_data)
assert_stream_load_json_correct (JsonParser *parser)
{
JsonParser *parser = JSON_PARSER (gobject);
GMainLoop *main_loop = user_data;
GError *error = NULL;
JsonNode *root;
JsonArray *array;
json_parser_load_from_stream_finish (parser, result, &error);
g_assert_no_error (error);
root = json_parser_get_root (parser);
g_assert (root != NULL);
g_assert (JSON_NODE_HOLDS_ARRAY (root));
......@@ -711,6 +704,21 @@ on_load_complete (GObject *gobject,
g_assert_cmpint (json_array_get_length (array), ==, 1);
g_assert (JSON_NODE_HOLDS_OBJECT (json_array_get_element (array, 0)));
g_assert (json_object_has_member (json_array_get_object_element (array, 0), "hello"));
}
static void
on_load_complete (GObject *gobject,
GAsyncResult *result,
gpointer user_data)
{
JsonParser *parser = JSON_PARSER (gobject);
GMainLoop *main_loop = user_data;
GError *error = NULL;
json_parser_load_from_stream_finish (parser, result, &error);
g_assert_no_error (error);
assert_stream_load_json_correct (parser);
g_main_loop_quit (main_loop);
}
......@@ -746,6 +754,59 @@ test_stream_async (void)
g_free (path);
}
/* Test json_parser_load_from_mapped_file() succeeds. */
static void
test_mapped (void)
{
GError *error = NULL;
JsonParser *parser = json_parser_new ();
char *path;
path = g_test_build_filename (G_TEST_DIST, "stream-load.json", NULL);
json_parser_load_from_mapped_file (parser, path, &error);
g_assert_no_error (error);
assert_stream_load_json_correct (parser);
g_object_unref (parser);
g_free (path);
}
/* Test json_parser_load_from_mapped_file() error handling for file I/O. */
static void
test_mapped_file_error (void)
{
GError *error = NULL;
JsonParser *parser = json_parser_new ();
json_parser_load_from_mapped_file (parser, "nope.json", &error);
g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
g_assert_null (json_parser_get_root (parser));
g_object_unref (parser);
}
/* Test json_parser_load_from_mapped_file() error handling for JSON parsing. */
static void
test_mapped_json_error (void)
{
GError *error = NULL;
JsonParser *parser = json_parser_new ();
char *path;
path = g_test_build_filename (G_TEST_DIST, "invalid.json", NULL);
json_parser_load_from_mapped_file (parser, path, &error);
g_assert_error (error, JSON_PARSER_ERROR, JSON_PARSER_ERROR_INVALID_BAREWORD);
g_assert_null (json_parser_get_root (parser));
g_object_unref (parser);
g_free (path);
}
int
main (int argc,
char *argv[])
......@@ -764,6 +825,9 @@ main (int argc,
g_test_add_func ("/parser/unicode-escape", test_unicode_escape);
g_test_add_func ("/parser/stream-sync", test_stream_sync);
g_test_add_func ("/parser/stream-async", test_stream_async);
g_test_add_func ("/parser/mapped", test_mapped);
g_test_add_func ("/parser/mapped/file-error", test_mapped_file_error);
g_test_add_func ("/parser/mapped/json-error", test_mapped_json_error);
return g_test_run ();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment