]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: buffer - Add buffer_replace().
authorStephan Bosch <stephan.bosch@dovecot.fi>
Thu, 14 Mar 2019 22:55:00 +0000 (23:55 +0100)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Tue, 10 Sep 2019 08:51:09 +0000 (11:51 +0300)
src/lib/buffer.c
src/lib/buffer.h
src/lib/test-buffer.c

index 2f84f72cdcf08af054bbd6f897b3994c24cd0a9f..f1883edb165a6223f160e320740aec9882894d2f 100644 (file)
@@ -233,6 +233,40 @@ void buffer_delete(buffer_t *_buf, size_t pos, size_t size)
        buffer_set_used_size(_buf, pos + end_size);
 }
 
+void buffer_replace(buffer_t *_buf, size_t pos, size_t size,
+                   const void *data, size_t data_size)
+{
+       struct real_buffer *buf = (struct real_buffer *)_buf;
+       size_t end_size;
+
+       if (pos >= buf->used) {
+               buffer_write(_buf, pos, data, data_size);
+               return;
+       }
+       end_size = buf->used - pos;
+
+       if (size < end_size) {
+               end_size -= size;
+               if (data_size == 0) {
+                       /* delete from between */
+                       memmove(buf->w_buffer + pos,
+                               buf->w_buffer + pos + size, end_size);
+               } else {
+                       /* insert */
+                       buffer_copy(_buf, pos + data_size, _buf, pos + size,
+                                   (size_t)-1);
+                       memcpy(buf->w_buffer + pos, data, data_size);
+               }
+       } else {
+               /* overwrite the end */
+               end_size = 0;
+               buffer_write(_buf, pos, data, data_size);
+       }
+
+       buffer_set_used_size(_buf, pos + data_size + end_size);
+}
+
+
 void buffer_write_zero(buffer_t *_buf, size_t pos, size_t data_size)
 {
        struct real_buffer *buf = (struct real_buffer *)_buf;
index 423fb9ba70ce0731775e4851d8e0f954bc3a6202..70b5ea7101081a83d3e3a2dff3f3f59b5e97d83f 100644 (file)
@@ -60,6 +60,11 @@ void buffer_insert(buffer_t *buf, size_t pos,
    deleted block may cross the current buffer size boundary, which is ignored.
  */
 void buffer_delete(buffer_t *buf, size_t pos, size_t size);
+/* Replace the data in the buffer with the indicated size at position pos with
+   the provided data. This is a more optimized version of
+   buffer_delete(buf, pos, size); buffer_insert(buf, pos, data, data_size); */
+void buffer_replace(buffer_t *buf, size_t pos, size_t size,
+                   const void *data, size_t data_size);
 
 /* Fill buffer with zero bytes. */
 void buffer_write_zero(buffer_t *buf, size_t pos, size_t data_size);
index 903e52609e9d6a5bbd5dbb854c4f976294bf46f1..baa404312466bf3367d87acc2781ba6ff72d0e34 100644 (file)
@@ -10,7 +10,7 @@ static void test_buffer_random(void)
        buffer_t *buf;
        unsigned char *p, testdata[BUF_TEST_SIZE], shadowbuf[BUF_TEST_SIZE];
        unsigned int i, shadowbuf_size;
-       size_t pos, pos2, size;
+       size_t pos, pos2, size, size2;
        int test = -1;
        bool zero;
 
@@ -29,7 +29,7 @@ static void test_buffer_random(void)
                        i_assert(buf->used < BUF_TEST_SIZE);
                }
 
-               test = i_rand_limit(6);
+               test = i_rand_limit(7);
                zero = i_rand_limit(10) == 0;
                switch (test) {
                case 0:
@@ -95,6 +95,32 @@ static void test_buffer_random(void)
                        }
                        break;
                case 4:
+                       pos = i_rand_limit(BUF_TEST_SIZE - 1);
+                       size = i_rand_limit(BUF_TEST_SIZE - pos);
+                       size2 = i_rand_limit(BUF_TEST_SIZE -
+                                            I_MAX(buf->used, pos));
+                       buffer_replace(buf, pos, size, testdata, size2);
+                       if (pos < shadowbuf_size) {
+                               if (pos + size > shadowbuf_size)
+                                       size = shadowbuf_size - pos;
+                               memmove(shadowbuf + pos,
+                                       shadowbuf + pos + size,
+                                       BUF_TEST_SIZE - (pos + size));
+
+                               shadowbuf_size -= size;
+                               memset(shadowbuf + shadowbuf_size, 0,
+                                      BUF_TEST_SIZE - shadowbuf_size);
+                       }
+                       memmove(shadowbuf + pos + size2,
+                               shadowbuf + pos,
+                               BUF_TEST_SIZE - (pos + size2));
+                       memcpy(shadowbuf + pos, testdata, size2);
+                       if (pos < shadowbuf_size)
+                               shadowbuf_size += size2;
+                       else
+                               shadowbuf_size = pos + size2;
+                       break;
+               case 5:
                        if (shadowbuf_size <= 1)
                                break;
                        pos = i_rand_limit(shadowbuf_size - 1); /* dest */
@@ -106,7 +132,7 @@ static void test_buffer_random(void)
                        if (pos > pos2 && pos + size > shadowbuf_size)
                                shadowbuf_size = pos + size;
                        break;
-               case 5:
+               case 6:
                        pos = i_rand_limit(BUF_TEST_SIZE - 1);
                        size = i_rand_limit(BUF_TEST_SIZE - pos);
                        p = buffer_get_space_unsafe(buf, pos, size);
@@ -271,10 +297,41 @@ static void test_buffer_truncate_bits(void)
        test_end();
 }
 
+static void test_buffer_replace(void)
+{
+       const char orig_input[] = "123456789";
+       const char data[] = "abcdefghij";
+       buffer_t *buf, *buf2;
+       unsigned int init_size, pos, size, data_size;
+
+       test_begin("buffer_replace()");
+       for (init_size = 0; init_size <= sizeof(orig_input)-1; init_size++) {
+               for (pos = 0; pos < sizeof(orig_input)+1; pos++) {
+                       for (size = 0; size < sizeof(orig_input)+1; size++) {
+                               for (data_size = 0; data_size <= sizeof(data)-1; data_size++) T_BEGIN {
+                                       buf = buffer_create_dynamic(pool_datastack_create(), 4);
+                                       buf2 = buffer_create_dynamic(pool_datastack_create(), 4);
+                                       buffer_append(buf, orig_input, init_size);
+                                       buffer_append(buf2, orig_input, init_size);
+
+                                       buffer_replace(buf, pos, size, data, data_size);
+                                       buffer_delete(buf2, pos, size);
+                                       buffer_insert(buf2, pos, data, data_size);
+                                       test_assert(buf->used == buf2->used &&
+                                                   memcmp(buf->data, buf2->data, buf->used) == 0);
+                               } T_END;
+                       }
+               }
+       }
+
+       test_end();
+}
+
 void test_buffer(void)
 {
        test_buffer_random();
        test_buffer_write();
        test_buffer_set_used_size();
        test_buffer_truncate_bits();
+       test_buffer_replace();
 }