[PATCH 04/12] util/string-map: add _notmuch_string_map_serialize

David Bremner david at tethera.net
Fri Jun 22 18:42:39 PDT 2018


The anticipated usage of this is to serialize a string map into the
data area of a xapian document
---
 test/T710-string-map.sh | 312 ++++++++++++++++++++++++++++++++++++++++
 util/string-map.c       |  42 ++++++
 util/string-map.h       |   7 +
 3 files changed, 361 insertions(+)

diff --git a/test/T710-string-map.sh b/test/T710-string-map.sh
index b2f65381..9c5c1d8e 100755
--- a/test/T710-string-map.sh
+++ b/test/T710-string-map.sh
@@ -114,4 +114,316 @@ testval1
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "serialize empty"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+    dump_map (map);
+    printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat<<EOF > EXPECTED
+== stdout ==
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+    _notmuch_string_map_append (map, "testkey1", "testval1");
+    _notmuch_string_map_append (map, "testkey2", "testval2");
+    _notmuch_string_map_append (map, "testkey1", "testval3");
+    dump_map (map);
+    printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat<<EOF > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+testkey1
+testval1
+testkey1
+testval3
+testkey2
+testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, key with embedded newline"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+    _notmuch_string_map_append (map, "testkey1", "testval1");
+    _notmuch_string_map_append (map, "testkey2", "testval2");
+    _notmuch_string_map_append (map, "testkey1", "testval3");
+    _notmuch_string_map_append (map, "testkey2\nreallynot", "testval4");
+    dump_map (map);
+    printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat<<EOF > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+key[3]=testkey2
+reallynot
+val[3]=testval4
+testkey1
+testval1
+testkey1
+testval3
+testkey2
+testval2
+testkey2\nreallynot
+testval4
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, key with embedded backslash"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+    _notmuch_string_map_append (map, "testkey1", "testval1");
+    _notmuch_string_map_append (map, "testkey2", "testval2");
+    _notmuch_string_map_append (map, "testkey1", "testval3");
+    _notmuch_string_map_append (map, "testkey2\\not", "testval4");
+    dump_map (map);
+    printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat<<'EOF' > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+key[3]=testkey2\not
+val[3]=testval4
+testkey1
+testval1
+testkey1
+testval3
+testkey2
+testval2
+testkey2\\not
+testval4
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, value with embedded newline"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+    _notmuch_string_map_append (map, "testkey1", "testval1");
+    _notmuch_string_map_append (map, "testkey2", "testval2");
+    _notmuch_string_map_append (map, "testkey1", "testval3");
+    _notmuch_string_map_append (map, "testkey2", "testval4\nvalue continues");
+    dump_map (map);
+    printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat<<EOF > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+key[3]=testkey2
+val[3]=testval4
+value continues
+testkey1
+testval1
+testkey1
+testval3
+testkey2
+testval2
+testkey2
+testval4\nvalue continues
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, key and value with embedded newline"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+    _notmuch_string_map_append (map, "testkey1", "testval1");
+    _notmuch_string_map_append (map, "testkey2", "testval2");
+    _notmuch_string_map_append (map, "testkey1\nkey continues", "testval3\nvalue continues");
+    dump_map (map);
+    printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat<<EOF > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+key continues
+val[1]=testval3
+value continues
+key[2]=testkey2
+val[2]=testval2
+testkey1
+testval1
+testkey1\nkey continues
+testval3\nvalue continues
+testkey2
+testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "serialize, key and value with embedded literal \n"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_create(ctx);
+    _notmuch_string_map_append (map, "testkey1", "testval1");
+    _notmuch_string_map_append (map, "testkey2", "testval2");
+    _notmuch_string_map_append (map, "testkey1\\nkey continues", "testval3\\nvalue continues");
+    dump_map (map);
+    printf ("%s",_notmuch_string_map_serialize (ctx, map));
+}
+EOF
+cat<<'EOF' > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1\nkey continues
+val[1]=testval3\nvalue continues
+key[2]=testkey2
+val[2]=testval2
+testkey1
+testval1
+testkey1\\nkey continues
+testval3\\nvalue continues
+testkey2
+testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "deserialize empty string"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_deserialize (ctx, "");
+    dump_map (map);
+}
+EOF
+cat<<EOF > EXPECTED
+== stdout ==
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "deserialize"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_deserialize (ctx,
+        "testkey1\n"
+        "testval1\n"
+        "testkey1\n"
+        "testval3\n"
+        "testkey2\n"
+        "testval2\n");
+    dump_map (map);
+}
+EOF
+cat<<EOF > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+val[1]=testval3
+key[2]=testkey2
+val[2]=testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "deserialize, key and value with embedded newline"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    notmuch_string_map_t *map = _notmuch_string_map_deserialize (ctx,
+        "testkey1\n"
+        "testval1\n"
+        "testkey1\\nkey continues\n"
+        "testval3\\nvalue continues\n"
+        "testkey2\n"
+        "testval2\n");
+    dump_map (map);
+}
+EOF
+cat<<EOF > EXPECTED
+== stdout ==
+key[0]=testkey1
+val[0]=testval1
+key[1]=testkey1
+key continues
+val[1]=testval3
+value continues
+key[2]=testkey2
+val[2]=testval2
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "deserialize, keys and values with embedded backslashes"
+cat c_head - c_tail <<'EOF' | test_C $
+{
+    const char * str =
+         "testkey1\n"
+         "testval1\\b\n"
+         "testkey1\n"
+         "testval3\\\n"
+         "testkey2\\\\not\n"
+         "testval2\n"
+         "testkey2\\toto\n"
+         "testval4\n";
+
+    notmuch_string_map_t *map = _notmuch_string_map_deserialize (ctx, str);
+    fputs(str, stdout);
+    dump_map (map);
+}
+EOF
+cat<<'EOF' > EXPECTED
+== stdout ==
+testkey1
+testval1\b
+testkey1
+testval3\
+testkey2\\not
+testval2
+testkey2\toto
+testval4
+key[0]=testkey1
+val[0]=testval1\b
+key[1]=testkey1
+val[1]=testval3\
+key[2]=testkey2\not
+val[2]=testval2
+key[3]=testkey2\toto
+val[3]=testval4
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
diff --git a/util/string-map.c b/util/string-map.c
index ad818207..ab0c42ab 100644
--- a/util/string-map.c
+++ b/util/string-map.c
@@ -226,3 +226,45 @@ _notmuch_string_map_iterator_destroy (notmuch_string_map_iterator_t *iterator)
 {
     talloc_free (iterator);
 }
+
+static const char *
+_append_escaped (void *ctx, const char *dest, const char *str) {
+    /* At worst we escape everything */
+    char *buf = talloc_zero_size (ctx, 2 * strlen(str) + 1);
+    char *ret;
+    int j = 0;
+    for (const char *cur = str; *cur; cur++){
+	if (*cur == '\n') {
+	    buf[j++] = '\\';
+	    buf[j++] = 'n';
+	} else if (*cur == '\\' && *(cur+1)== 'n') {
+	    buf[j++] = '\\';
+	    buf[j++] = '\\';
+	} else {
+	    buf[j++] = *cur;
+	}
+    }
+    /* this NUL is already there, but better safe than sorry */
+    buf[j] = '\0';
+    ret = talloc_asprintf (ctx, "%s%s", dest, buf);
+    talloc_free (buf);
+    return ret;
+}
+
+const char *
+_notmuch_string_map_serialize (void* ctx, notmuch_string_map_t *map)
+{
+    const char *ret;
+
+    _notmuch_string_map_sort (map);
+
+    ret=talloc_strdup(ctx, "");
+    for (size_t i=0; i < map->length; i++) {
+	ret = _append_escaped (ctx, ret, map->pairs[i].key);
+	ret = talloc_asprintf(ctx, "%s\n", ret);
+	ret = _append_escaped (ctx, ret, map->pairs[i].value);
+	ret = talloc_asprintf(ctx, "%s\n", ret);
+    }
+
+    return ret;
+}
diff --git a/util/string-map.h b/util/string-map.h
index 42d16da4..9baf3530 100644
--- a/util/string-map.h
+++ b/util/string-map.h
@@ -33,4 +33,11 @@ _notmuch_string_map_iterator_value (notmuch_string_map_iterator_t *iterator);
 
 void
 _notmuch_string_map_iterator_destroy (notmuch_string_map_iterator_t *iterator);
+
+/*
+ * encode map as newline delimited string "key1\nval1\key2\nval2\n..."
+ * newlines will be escaped as "\n", and "\n" will be escaped as "\\n".
+ */
+const char *
+_notmuch_string_map_serialize (void *ctx, notmuch_string_map_t *map);
 #endif
-- 
2.17.1



More information about the notmuch mailing list