[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