From: Tor Didriksen Date: February 7 2011 1:59pm Subject: bzr commit into mysql-trunk branch (tor.didriksen:3605) WL#5774 List-Archive: http://lists.mysql.com/commits/130580 Message-Id: <20110207135955.CC348376D@atum07.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============5891621631140051580==" --===============5891621631140051580== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///export/home/didrik/repo/trunk-dynarray/ based on revid:tor.didriksen@stripped 3605 Tor Didriksen 2011-02-07 WL #5774 Decrease number of malloc's for normal DML queries One of the malloc's was due to DYNAMIC_ARRAY keyuse; Here's an experiment to use an array allocated in thd->mem_root instead. added: sql/mem_root_array.h unittest/gunit/dynarray-t.cc modified: sql/sql_select.cc sql/sql_select.h sql/sql_test.cc sql/sql_test.h unittest/gunit/CMakeLists.txt === added file 'sql/mem_root_array.h' --- a/sql/mem_root_array.h 1970-01-01 00:00:00 +0000 +++ b/sql/mem_root_array.h 2011-02-07 13:59:50 +0000 @@ -0,0 +1,132 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#ifndef MEM_ROOT_ARRAY_INCLUDED +#define MEM_ROOT_ARRAY_INCLUDED + +#include + +/** + A typesafe replacement for DYNAMIC_ARRAY. + We use MEM_ROOT for allocating storage, rather than the C++ heap. + The interface is chosen to be similar to std::vector. +*/ +template +class Mem_root_array +{ +public: + Mem_root_array() + : m_root(NULL), m_array(NULL), m_size(0), m_capacity(0) + {} + + Mem_root_array(MEM_ROOT *root) + : m_root(root), m_array(NULL), m_size(0), m_capacity(0) + {} + + ~Mem_root_array() + { + clear(); + } + + void set_mem_root(MEM_ROOT *root) { m_root= root; } + + Element_type &at(size_t n) + { + DBUG_ASSERT(n < size()); + return m_array[n]; + } + + const Element_type &at(size_t n) const + { + DBUG_ASSERT(n < size()); + return m_array[n]; + } + + // Clear array contents. + void clear() + { + chop(0); + } + + // Chop the tail off the array. + void chop(size_t pos) + { + if (pos >= m_size) + return; + for (size_t ix= pos; pos < m_size; ++pos) + { + Element_type *p= &m_array[ix]; + p->~Element_type(); + } + m_size= pos; + } + + // Reserves space for array elements. + // Copies over existing elements, in case we are re-expanding the array. + bool reserve(size_t n) + { + if (n <= m_capacity) + return false; + + void *mem= alloc_root(m_root, n * element_size()); + if (!mem) + return true; + Element_type *array= static_cast(mem); + + // Copy all the existing elements into the new array. + for (size_t ix= 0; ix < m_size; ++ix) + { + Element_type *new_p= &array[ix]; + Element_type *old_p= &m_array[ix]; + new (new_p) Element_type(*old_p); + old_p->~Element_type(); // Delete the old element. + } + + // Forget the old array. + m_array= array; + m_capacity= n; + return false; + } + + // Adds a new element at the end of the array, after its current last + // element. The content of this new element is initialized to a copy of + // the input argument. + bool push_back(const Element_type &element) + { + if (0 == m_capacity && reserve(10)) + return true; + if (m_size == m_capacity && reserve(m_capacity * 2)) + return true; + Element_type *p= &m_array[m_size++]; + new (p) Element_type(element); + return false; + } + + size_t element_size() const { return sizeof(Element_type); } + size_t size() const { return m_size; } + size_t capacity() const { return m_capacity; } + + Element_type *get_buffer_for_sorting() { return m_array; } + +private: + MEM_ROOT *m_root; + Element_type *m_array; + size_t m_size; + size_t m_capacity; +}; + + +#endif // MEM_ROOT_ARRAY_INCLUDED === modified file 'sql/sql_select.cc' --- a/sql/sql_select.cc 2011-02-07 09:46:53 +0000 +++ b/sql/sql_select.cc 2011-02-07 13:59:50 +0000 @@ -63,11 +63,11 @@ const char *join_type_str[]={ "UNKNOWN", struct st_sargable_param; -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array); +static void optimize_keyuse(JOIN *join, Key_use_array *keyuse_array); static bool make_join_statistics(JOIN *join, TABLE_LIST *leaves, Item *conds, - DYNAMIC_ARRAY *keyuse); + Key_use_array *keyuse); static bool optimize_semijoin_nests(JOIN *join, table_map all_table_map); -static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, +static bool update_ref_and_keys(THD *thd, Key_use_array *keyuse, JOIN_TAB *join_tab, uint tables, Item *conds, COND_EQUAL *cond_equal, @@ -3437,7 +3437,7 @@ JOIN::destroy() while ((sj_nest= sj_list_it++)) sj_nest->sj_mat_exec= NULL; - delete_dynamic(&keyuse); + keyuse.clear(); delete procedure; DBUG_RETURN(error); } @@ -4594,7 +4594,7 @@ static uint get_tmp_table_rec_length(Lis static bool make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, Item *conds, - DYNAMIC_ARRAY *keyuse_array) + Key_use_array *keyuse_array) { int error; TABLE *table; @@ -5981,7 +5981,7 @@ max_part_bit(key_part_map bits) */ static bool -add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field) +add_key_part(Key_use_array *keyuse_array,KEY_FIELD *key_field) { Field *field=key_field->field; TABLE *form= field->table; @@ -6011,7 +6011,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array key_field->null_rejecting, key_field->cond_guard, key_field->sj_pred_no); - if (insert_dynamic(keyuse_array, &keyuse)) + if (keyuse_array->push_back(keyuse)) return TRUE; } } @@ -6024,7 +6024,7 @@ add_key_part(DYNAMIC_ARRAY *keyuse_array #define FT_KEYPART (MAX_REF_PARTS+10) static bool -add_ft_keys(DYNAMIC_ARRAY *keyuse_array, +add_ft_keys(Key_use_array *keyuse_array, JOIN_TAB *stat,Item *cond,table_map usable_tables) { Item_func_match *cond_func=NULL; @@ -6086,7 +6086,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, false, // null_rejecting NULL, // cond_guard UINT_MAX); // sj_pred_no - return insert_dynamic(keyuse_array, &keyuse); + return keyuse_array->push_back(keyuse); } @@ -6202,7 +6202,7 @@ static void add_key_fields_for_nj(JOIN * */ static bool -update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, +update_ref_and_keys(THD *thd, Key_use_array *keyuse,JOIN_TAB *join_tab, uint tables, Item *cond, COND_EQUAL *cond_equal, table_map normal_tables, SELECT_LEX *select_lex, SARGABLE_PARAM **sargables) @@ -6245,7 +6245,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR /* set a barrier for the array of SARGABLE_PARAM */ (*sargables)[0].field= 0; - if (my_init_dynamic_array(keyuse, sizeof(Key_use), 20, 64)) + keyuse->set_mem_root(thd->mem_root); + if (keyuse->reserve(20)) return TRUE; if (cond) { @@ -6320,21 +6321,22 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR used in the query, we drop the partial key parts from consideration). Special treatment for ft-keys. */ - if (keyuse->elements) + if (keyuse->size()) { Key_use *save_pos, *use; - my_qsort(keyuse->buffer, keyuse->elements, sizeof(Key_use), + Key_use *buf= keyuse->get_buffer_for_sorting(); + my_qsort(buf, keyuse->size(), keyuse->element_size(), reinterpret_cast(sort_keyuse)); const Key_use key_end(NULL, NULL, 0, 0, 0, 0, 0, 0, false, NULL, 0); - if (insert_dynamic(keyuse, &key_end)) // added for easy testing + if (keyuse->push_back(key_end)) // added for easy testing return TRUE; - use= save_pos= dynamic_element(keyuse, 0, Key_use *); + use= save_pos= &keyuse->at(0); const Key_use *prev= &key_end; found_eq_constant=0; - for (i=0 ; i < keyuse->elements-1 ; i++,use++) + for (i=0 ; i < keyuse->size()-1 ; i++,use++) { if (!use->used_tables && use->optimize != KEY_OPTIMIZE_REF_OR_NULL) use->table->const_key_parts[use->key]|= use->keypart_map; @@ -6367,9 +6369,9 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR use->table->reginfo.join_tab->checked_keys.set_bit(use->key); save_pos++; } - i= (uint) (save_pos - (Key_use *)keyuse->buffer); - (void) set_dynamic(keyuse, &key_end, i); - keyuse->elements=i; + i= (uint) (save_pos - &keyuse->at(0)); + keyuse->at(i) = key_end; + keyuse->chop(i); } DBUG_EXECUTE("opt", print_keyuse_array(keyuse);); return FALSE; @@ -6379,12 +6381,11 @@ update_ref_and_keys(THD *thd, DYNAMIC_AR Update some values in keyuse for faster choose_plan() loop. */ -static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) +static void optimize_keyuse(JOIN *join, Key_use_array *keyuse_array) { - Key_use *end, *keyuse= dynamic_element(keyuse_array, 0, Key_use *); - - for (end= keyuse+ keyuse_array->elements ; keyuse < end ; keyuse++) + for (size_t ix= 0; ix < keyuse_array->size(); ++ix) { + Key_use *keyuse= &keyuse_array->at(ix); table_map map; /* If we find a ref, assume this table matches a proportional === modified file 'sql/sql_select.h' --- a/sql/sql_select.h 2011-01-14 08:20:08 +0000 +++ b/sql/sql_select.h 2011-02-07 13:59:50 +0000 @@ -34,6 +34,7 @@ #include "records.h" /* READ_RECORD */ #include "opt_range.h" /* SQL_SELECT, QUICK_SELECT_I */ +#include "mem_root_array.h" /* Values in optimize */ #define KEY_OPTIMIZE_EXISTS 1 @@ -47,6 +48,8 @@ */ class Key_use { public: + Key_use() { memset(this, 0, sizeof(*this)); } + Key_use(TABLE *table_arg, Item *val_arg, table_map used_tables_arg, uint key_arg, uint keypart_arg, uint optimize_arg, key_part_map keypart_map_arg, ha_rows ref_table_rows_arg, @@ -97,6 +100,10 @@ public: uint sj_pred_no; }; + +typedef Mem_root_array Key_use_array; +void print_keyuse_array(Key_use_array *keyuse_array); + class store_key; typedef struct st_table_ref : public Sql_alloc @@ -1772,7 +1779,10 @@ public: bool skip_sort_order; bool need_tmp, hidden_group_fields; - DYNAMIC_ARRAY keyuse; + + // DYNAMIC_ARRAY keyuse; + Key_use_array keyuse; + List all_fields; ///< to store all fields that used in query ///Above list changed to use temporary table List tmp_all_fields1, tmp_all_fields2, tmp_all_fields3; === modified file 'sql/sql_test.cc' --- a/sql/sql_test.cc 2010-11-05 22:19:41 +0000 +++ b/sql/sql_test.cc 2011-02-07 13:59:50 +0000 @@ -239,7 +239,7 @@ TEST_join(JOIN *join) #define FT_KEYPART (MAX_REF_PARTS+10) -void print_keyuse(Key_use *keyuse) +void print_keyuse(const Key_use *keyuse) { char buff[256]; char buf2[64]; @@ -276,6 +276,16 @@ void print_keyuse_array(DYNAMIC_ARRAY *k (dynamic_array_ptr(keyuse_array, i))); } +void print_keyuse_array(Key_use_array *keyuse_array) +{ + DBUG_LOCK_FILE; + fprintf(DBUG_FILE, "Key_use array (%d elements)\n", + (int) keyuse_array->size()); + DBUG_UNLOCK_FILE; + for(uint i=0; i < keyuse_array->size(); i++) + print_keyuse(&keyuse_array->at(i)); +} + /* Print the current state during query optimization. === modified file 'sql/sql_test.h' --- a/sql/sql_test.h 2010-08-19 07:10:58 +0000 +++ b/sql/sql_test.h 2011-02-07 13:59:50 +0000 @@ -20,6 +20,7 @@ class JOIN; struct TABLE_LIST; +class Key_use; typedef class st_select_lex SELECT_LEX; typedef struct st_sort_field SORT_FIELD; @@ -32,6 +33,7 @@ void print_plan(JOIN* join,uint idx, dou void dump_TABLE_LIST_graph(SELECT_LEX *select_lex, TABLE_LIST* tl); void print_sjm(TABLE_LIST *emb_sj_nest); void print_keyuse_array(DYNAMIC_ARRAY *keyuse_array); +void print_keyuse(const Key_use *keyuse); #endif void mysql_print_status(); === modified file 'unittest/gunit/CMakeLists.txt' --- a/unittest/gunit/CMakeLists.txt 2011-02-07 13:03:47 +0000 +++ b/unittest/gunit/CMakeLists.txt 2011-02-07 13:59:50 +0000 @@ -210,6 +210,7 @@ ENDIF() SET(TESTS bounded_queue dbug + dynarray mdl mdl_mytap my_decimal === added file 'unittest/gunit/dynarray-t.cc' --- a/unittest/gunit/dynarray-t.cc 1970-01-01 00:00:00 +0000 +++ b/unittest/gunit/dynarray-t.cc 2011-02-07 13:59:50 +0000 @@ -0,0 +1,315 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +// First include (the generated) my_config.h, to get correct platform defines, +// then gtest.h (before any other MySQL headers), to avoid min() macros etc ... +#include "my_config.h" +#include + +#include +#include +#include + +#include "sql_select.h" +#include "mem_root_array.h" +/** + WL #5774 Decrease number of malloc's for normal DML queries + One of the malloc's was due to DYNAMIC_ARRAY keyuse; + Here's an experiment to use an array allocated in thd->mem_root instead. + + This is the interface we need to support: + struct XX_DYNAMIC_ARRAY + { + .... + }; + + void delete_dynamic(XX_DYNAMIC_ARRAY*); + dynamic_element(array,array_index,type); + bool init_dynamic_array2(XX_DYNAMIC_ARRAY*, uint, void*, uint, uint); + bool insert_dynamic(XX_DYNAMIC_ARRAY*, const Key_use*); + bool set_dynamic(XX_DYNAMIC_ARRAY*, const void*, uint); + void print_keyuse_array(XX_DYNAMIC_ARRAY*); +*/ + +pthread_key(MEM_ROOT**, THR_MALLOC); +pthread_key(THD*, THR_THD); + +extern "C" void sql_alloc_error_handler(void) +{ + ADD_FAILURE(); +} + +// Cut'n paste this function from sql_select.cc, +// to avoid linking in the entire server for this unit test. +inline int sort_keyuse(Key_use *a, Key_use *b) +{ + int res; + if (a->table->tablenr != b->table->tablenr) + return (int) (a->table->tablenr - b->table->tablenr); + if (a->key != b->key) + return (int) (a->key - b->key); + if (a->keypart != b->keypart) + return (int) (a->keypart - b->keypart); + // Place const values before other ones + if ((res= test((a->used_tables & ~OUTER_REF_TABLE_BIT)) - + test((b->used_tables & ~OUTER_REF_TABLE_BIT)))) + return res; + /* Place rows that are not 'OPTIMIZE_REF_OR_NULL' first */ + return (int) ((a->optimize & KEY_OPTIMIZE_REF_OR_NULL) - + (b->optimize & KEY_OPTIMIZE_REF_OR_NULL)); +} + +// Rewrite of sort_keyuse() to comparison operator for use by std::less<> +inline bool operator<(const Key_use &a, const Key_use &b) +{ + if (a.table->tablenr != b.table->tablenr) + return a.table->tablenr < b.table->tablenr; + if (a.key != b.key) + return a.key < b.key; + if (a.keypart != b.keypart) + return a.keypart < b.keypart; + const bool atab = test((a.used_tables & ~OUTER_REF_TABLE_BIT)); + const bool btab = test((b.used_tables & ~OUTER_REF_TABLE_BIT)); + if (atab != btab) + return atab < btab; + return + ((a.optimize & KEY_OPTIMIZE_REF_OR_NULL) < + (b.optimize & KEY_OPTIMIZE_REF_OR_NULL)); +} + +namespace { + +// Play around with these constants to see std::sort speedup vs. my_qsort. +const int num_elements= 200; +const int num_iterations= 1; + +// We generate some random data at startup, for testing of sorting. +Key_use test_data[num_elements]; +TABLE table_list[num_elements]; + + +class DynArrayTest : public ::testing::Test +{ +public: + static void SetUpTestCase() + { + int ix; + for (ix= 0; ix < num_elements; ++ix) + { + table_list[ix].tablenr= ix % 3; + test_data[ix]= + Key_use(&table_list[ix], + NULL, // Item *val + 0, // table_map used_tables + ix % 4, // uint key + ix % 2, // uint keypart + 0, // uint optimize + 0, // keypart_map + 0, // ha_rows ref_table_rows + true, // bool null_rejecting + NULL, // bool *cond_guard + 0 // uint sj_pred_no + ); + } + std::random_shuffle(&test_data[0], &test_data[num_elements]); + } + + virtual void SetUp() + { + my_init_dynamic_array(&m_keyuse_dyn, sizeof(Key_use), num_elements, 64); + m_keyuse_vec.reserve(num_elements); + } + + void insert_and_sort_dynamic() + { + reset_dynamic(&m_keyuse_dyn); + for (int ix= 0; ix < num_elements; ++ix) + { + insert_dynamic(&m_keyuse_dyn, &test_data[ix]); + } + my_qsort(m_keyuse_dyn.buffer, m_keyuse_dyn.elements, sizeof(Key_use), + reinterpret_cast(sort_keyuse)); + } + + void insert_and_sort_vector() + { + m_keyuse_vec.clear(); + for (int ix= 0; ix < num_elements; ++ix) + { + m_keyuse_vec.push_back(test_data[ix]); + } + std::sort(m_keyuse_vec.begin(), m_keyuse_vec.end(), std::less()); + } + + DYNAMIC_ARRAY m_keyuse_dyn; + std::vector m_keyuse_vec; +}; + + +void print_key(uint ix, Key_use *key) +{ + fprintf(stdout, "ix %d table %d key %d\n", ix, key->table->tablenr, key->key); + fflush(stdout); +} + +void print_array(std::vector *arr) +{ + for (uint ix= 0; ix < arr->size(); ++ix) + { + Key_use &key_use = (*arr)[ix]; + print_key(ix, &key_use); + } +} + +void print_array(DYNAMIC_ARRAY *arr) +{ + for (uint ix= 0; ix < arr->elements; ++ix) + { + Key_use key_use; + void *p = &key_use; + get_dynamic(arr, static_cast(p), ix); + print_key(ix, &key_use); + } +} + +void print_array(Mem_root_array *arr) +{ + for (uint ix= 0; ix < arr->size(); ++ix) + { + Key_use &key_use= arr->at(ix); + print_key(ix, &key_use); + } +} + + +// Test insert_dynamic() and my_qsort(). +TEST_F(DynArrayTest, DynArray) +{ + for (int ix= 0; ix < num_iterations; ++ix) + insert_and_sort_dynamic(); + // print_array(&m_keyuse_dyn); +} + + +// Test vector::push_back() and std::sort() +TEST_F(DynArrayTest, Vector) +{ + for (int ix= 0; ix < num_iterations; ++ix) + insert_and_sort_vector(); + // print_array(&m_keyuse_vec); +} + + +class MemRootTest : public ::testing::Test +{ +protected: + MemRootTest() + : m_mem_root_p(&m_mem_root), m_array(m_mem_root_p) + {} + + virtual void SetUp() + { + init_sql_alloc(&m_mem_root, 1024, 0); + ASSERT_EQ(0, my_pthread_setspecific_ptr(THR_MALLOC, &m_mem_root_p)); + MEM_ROOT *root= *my_pthread_getspecific_ptr(MEM_ROOT**, THR_MALLOC); + ASSERT_EQ(root, m_mem_root_p); + + m_array.reserve(num_elements); + } + + virtual void TearDown() + { + free_root(&m_mem_root, MYF(0)); + } + + static void SetUpTestCase() + { + ASSERT_EQ(0, pthread_key_create(&THR_THD, NULL)); + ASSERT_EQ(0, pthread_key_create(&THR_MALLOC, NULL)); + } + + static void TearDownTestCase() + { + pthread_key_delete(THR_THD); + pthread_key_delete(THR_MALLOC); + } + + void insert_and_sort_mysys() + { + m_array.clear(); + for (int ix= 0; ix < num_elements; ++ix) + { + m_array.push_back(test_data[ix]); + } + Key_use *buf= m_array.get_buffer_for_sorting(); + my_qsort(buf, m_array.size(), m_array.element_size(), + reinterpret_cast(sort_keyuse)); + } + + void insert_and_sort_std() + { + m_array.clear(); + for (int ix= 0; ix < num_elements; ++ix) + { + m_array.push_back(test_data[ix]); + } + Key_use *buf= m_array.get_buffer_for_sorting(); + std::sort(&buf[0], &buf[m_array.size()], std::less()); + } + + MEM_ROOT m_mem_root; + MEM_ROOT *m_mem_root_p; + Mem_root_array m_array; +}; + + +// Test Mem_root_array::push_back() and my_qsort() +TEST_F(MemRootTest, KeyUseMysys) +{ + for (int ix= 0; ix < num_iterations; ++ix) + insert_and_sort_mysys(); + // print_array(&m_array); +} + + +// Test Mem_root_array::push_back() and std::sort() +TEST_F(MemRootTest, KeyUseStd) +{ + for (int ix= 0; ix < num_iterations; ++ix) + insert_and_sort_std(); + // print_array(&m_array); +} + + +// Test that Mem_root_array re-expanding works. +TEST_F(MemRootTest, Reserve) +{ + Mem_root_array intarr; + intarr.set_mem_root(m_mem_root_p); + intarr.reserve(2); + for (uint ix=0; ix < 20; ++ix) + { + EXPECT_EQ(ix, intarr.size()); + EXPECT_FALSE(intarr.push_back(ix)); + EXPECT_EQ(ix, intarr.at(ix)); + } + for (uint ix=0; ix < 20; ++ix) + { + EXPECT_EQ(ix, intarr.at(ix)); + } +} + + +} --===============5891621631140051580== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/tor.didriksen@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: tor.didriksen@stripped\ # uf0io3xbqk6hscza # target_branch: file:///export/home/didrik/repo/trunk-dynarray/ # testament_sha1: bba3f11e3f91249af502ec8268c1afa28c536bcc # timestamp: 2011-02-07 14:59:55 +0100 # base_revision_id: tor.didriksen@stripped\ # lmjksir7xpbf3sth # # Begin bundle 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 --===============5891621631140051580==--