Merge lp:~jameinel/bzr/2.1-static-tuple-btree-string-intern into lp:bzr
- 2.1-static-tuple-btree-string-intern
- Merge into bzr.dev
Status: | Merged |
---|---|
Approved by: | Andrew Bennetts |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~jameinel/bzr/2.1-static-tuple-btree-string-intern |
Merge into: | lp:bzr |
Diff against target: |
553 lines 12 files modified
NEWS (+13/-7) bzrlib/_bencode_pyx.pyx (+9/-1) bzrlib/_btree_serializer_pyx.pyx (+80/-38) bzrlib/_static_tuple_c.c (+8/-2) bzrlib/btree_index.py (+2/-0) bzrlib/builtins.py (+4/-1) bzrlib/index.py (+4/-1) bzrlib/repository.py (+7/-0) bzrlib/static_tuple.py (+25/-0) bzrlib/tests/test__static_tuple.py (+21/-0) bzrlib/util/_bencode_py.py (+7/-0) setup.py (+2/-1) |
To merge this branch: | bzr merge lp:~jameinel/bzr/2.1-static-tuple-btree-string-intern |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Bennetts | Approve | ||
bzr-core | Pending | ||
Review via email: mp+13296@code.launchpad.net |
Commit message
Description of the change
John A Meinel (jameinel) wrote : | # |
Andrew Bennetts (spiv) wrote : | # |
384 + refs_as_tuples = tuple([
385 + for ref_list in node[3]])
I wonder if it would be worth adding a convenience method, perhaps StaticTuple.
431 + # I don't believe we can define a method by which
432 + # (prefix,) + StaticTuple will work, though we could
In plain Python you could define an __radd__ for this, so surely there's a way to do this in C?
class T(object):
def __radd__(self, other):
return 'haha!'
t = T()
print ('tuple',) + t # prints 'haha!'
You may need to do something odd like provide the nb_add slot, even though this isn't really a numeric type, but I think that's ok. (All pure python classes would have that I think, even the non-numeric ones, so presumably having tp_as_number filled doesn't automatically make Python do dumb things.)
I think we can live without this, but it would be nice.
488 + k1 = stuple(
489 + stuple('<email address hidden>',))
490 + k2 = stuple(
491 + stuple('<email address hidden>',)),
492 + stuple('<email address hidden>',))
This test data is needlessly complex and hard to read. Why not e.g.:
k1 = stuple(
k2 = stuple(
Which is structurally the same and much easier to follow.
John A Meinel (jameinel) wrote : | # |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Andrew Bennetts wrote:
> Review: Approve
> 384 + refs_as_tuples = tuple([
> 385 + for ref_list in node[3]])
>
> I wonder if it would be worth adding a convenience method, perhaps StaticTuple.
As for "as_tuples()" I would be fine just extending ".as_tuple()" to do
exactly that. The main restriction is that we may not always have tuples
at this point.
At least so far, tuple is interchangeable w/ StaticTuple.
>
> 431 + # I don't believe we can define a method by which
> 432 + # (prefix,) + StaticTuple will work, though we could
>
> In plain Python you could define an __radd__ for this, so surely there's a way to do this in C?
>
> class T(object):
> def __radd__(self, other):
> return 'haha!'
> t = T()
>
> print ('tuple',) + t # prints 'haha!'
>
Tuple uses "tp_as_
didn't think worked the other way. But thanks for pointing me to this,
I'll look into it.
> You may need to do something odd like provide the nb_add slot, even though this isn't really a numeric type, but I think that's ok. (All pure python classes would have that I think, even the non-numeric ones, so presumably having tp_as_number filled doesn't automatically make Python do dumb things.)
>
> I think we can live without this, but it would be nice.
Actually, the main reason I added the comment is because I expect things
to fail at that point, but I haven't gotten a test case to trigger it,
and it also won't trigger with --2a formats... (They don't have missing
compression parents.)
>
> 488 + k1 = stuple(
> 489 + stuple('<email address hidden>',))
> 490 + k2 = stuple(
> 491 + stuple('<email address hidden>',)),
> 492 + stuple('<email address hidden>',))
>
> This test data is needlessly complex and hard to read. Why not e.g.:
>
> k1 = stuple(
> k2 = stuple(
>
> Which is structurally the same and much easier to follow.
Sure. I did the above because that was the actual data I was getting. Of
course, I've since narrowed it down to a bug in interning....
Anyway, I'm happy to simplify it, and should have done so before submitting.
John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://
iEYEARECAAYFAkr
RwcAni/
=ZhaX
-----END PGP SIGNATURE-----
Andrew Bennetts (spiv) wrote : | # |
[...]
> > In plain Python you could define an __radd__ for this, so surely there's a
> > way to do this in C?
[...]
> > You may need to do something odd like provide the nb_add slot, even though
> > this isn't really a numeric type, but I think that's ok. (All pure python
> > classes would have that I think, even the non-numeric ones, so presumably
> > having tp_as_number filled doesn't automatically make Python do dumb
> > things.)
By the way, because lack of support for tuple + StaticTuple caused all the
babune builders to go red, I took a look at this. (specifically,
bzrlib.
was failing)
You actually need nb_coerce as well as nb_add. Here's a rough patch that does
this. The error it gives when you try to add a plain tuple with incompatible
elements (e.g. ints) is probably not ideal, but it works.
-Andrew.
1 | === modified file 'bzrlib/_static_tuple_c.c' | |||
2 | --- bzrlib/_static_tuple_c.c 2009-10-15 18:18:44 +0000 | |||
3 | +++ bzrlib/_static_tuple_c.c 2009-10-16 08:24:36 +0000 | |||
4 | @@ -513,6 +513,78 @@ | |||
5 | 513 | "Check to see if this tuple has been interned.\n"; | 513 | "Check to see if this tuple has been interned.\n"; |
6 | 514 | 514 | ||
7 | 515 | 515 | ||
8 | 516 | int | ||
9 | 517 | StaticTuple_coerce(PyObject **v, PyObject **w) | ||
10 | 518 | { | ||
11 | 519 | StaticTuple *st; | ||
12 | 520 | if (PyTuple_Check(*v)) { | ||
13 | 521 | st = (StaticTuple*) StaticTuple_new_constructor( | ||
14 | 522 | &StaticTuple_Type, *v, NULL); | ||
15 | 523 | if (!st) | ||
16 | 524 | return -1; | ||
17 | 525 | Py_INCREF(st); | ||
18 | 526 | *v = (PyObject*)st; | ||
19 | 527 | } else if (StaticTuple_CheckExact(*v)) | ||
20 | 528 | Py_INCREF(*v); | ||
21 | 529 | else | ||
22 | 530 | return 1; | ||
23 | 531 | |||
24 | 532 | if (PyTuple_Check(*w)) { | ||
25 | 533 | st = (StaticTuple*) StaticTuple_new_constructor( | ||
26 | 534 | &StaticTuple_Type, *w, NULL); | ||
27 | 535 | if (!st) | ||
28 | 536 | return -1; | ||
29 | 537 | Py_INCREF(st); | ||
30 | 538 | *w = (PyObject*)st; | ||
31 | 539 | } else if (StaticTuple_CheckExact(*w)) | ||
32 | 540 | Py_INCREF(*w); | ||
33 | 541 | else | ||
34 | 542 | return 1; | ||
35 | 543 | return 0; | ||
36 | 544 | } | ||
37 | 545 | |||
38 | 546 | static PyObject * | ||
39 | 547 | StaticTuple_add(PyObject *v, PyObject *w) | ||
40 | 548 | { | ||
41 | 549 | PyObject *v_t = NULL, *w_t = NULL; | ||
42 | 550 | PyObject *tmp_tuple, *result; | ||
43 | 551 | /* StaticTuples and plain tuples may be added (concatenated) to | ||
44 | 552 | * StaticTuples. | ||
45 | 553 | */ | ||
46 | 554 | if (StaticTuple_CheckExact(v)) { | ||
47 | 555 | v_t = StaticTuple_as_tuple((StaticTuple*)v); | ||
48 | 556 | if (!v_t) | ||
49 | 557 | goto fail; | ||
50 | 558 | } else if (PyTuple_Check(v)) | ||
51 | 559 | v_t = v; | ||
52 | 560 | else | ||
53 | 561 | goto not_imp; | ||
54 | 562 | |||
55 | 563 | if (StaticTuple_CheckExact(w)) { | ||
56 | 564 | w_t = StaticTuple_as_tuple((StaticTuple*)w); | ||
57 | 565 | if (!w_t) | ||
58 | 566 | goto fail; | ||
59 | 567 | } else if (PyTuple_Check(w)) | ||
60 | 568 | w_t = w; | ||
61 | 569 | else | ||
62 | 570 | goto not_imp; | ||
63 | 571 | |||
64 | 572 | tmp_tuple = PySequence_Concat(v_t, w_t); | ||
65 | 573 | result = StaticTuple_new_constructor(&StaticTuple_Type, tmp_tuple, NULL); | ||
66 | 574 | Py_DECREF(tmp_tuple); | ||
67 | 575 | Py_INCREF(result); | ||
68 | 576 | return result; | ||
69 | 577 | |||
70 | 578 | not_imp: | ||
71 | 579 | Py_XDECREF(v_t); | ||
72 | 580 | Py_XDECREF(w_t); | ||
73 | 581 | return Py_NotImplemented; | ||
74 | 582 | fail: | ||
75 | 583 | Py_XDECREF(v_t); | ||
76 | 584 | Py_XDECREF(w_t); | ||
77 | 585 | return NULL; | ||
78 | 586 | } | ||
79 | 587 | |||
80 | 516 | static PyObject * | 588 | static PyObject * |
81 | 517 | StaticTuple_item(StaticTuple *self, Py_ssize_t offset) | 589 | StaticTuple_item(StaticTuple *self, Py_ssize_t offset) |
82 | 518 | { | 590 | { |
83 | @@ -574,6 +646,29 @@ | |||
84 | 574 | {NULL, NULL} /* sentinel */ | 646 | {NULL, NULL} /* sentinel */ |
85 | 575 | }; | 647 | }; |
86 | 576 | 648 | ||
87 | 649 | |||
88 | 650 | static PyNumberMethods StaticTuple_as_number = { | ||
89 | 651 | (binaryfunc) StaticTuple_add, /* nb_add */ | ||
90 | 652 | 0, /* nb_subtract */ | ||
91 | 653 | 0, /* nb_multiply */ | ||
92 | 654 | 0, /* nb_divide */ | ||
93 | 655 | 0, /* nb_remainder */ | ||
94 | 656 | 0, /* nb_divmod */ | ||
95 | 657 | 0, /* nb_power */ | ||
96 | 658 | 0, /* nb_negative */ | ||
97 | 659 | 0, /* nb_positive */ | ||
98 | 660 | 0, /* nb_absolute */ | ||
99 | 661 | 0, /* nb_nonzero */ | ||
100 | 662 | 0, /* nb_invert */ | ||
101 | 663 | 0, /* nb_lshift */ | ||
102 | 664 | 0, /* nb_rshift */ | ||
103 | 665 | 0, /* nb_and */ | ||
104 | 666 | 0, /* nb_xor */ | ||
105 | 667 | 0, /* nb_or */ | ||
106 | 668 | StaticTuple_coerce, /* nb_coerce */ | ||
107 | 669 | }; | ||
108 | 670 | |||
109 | 671 | |||
110 | 577 | static PySequenceMethods StaticTuple_as_sequence = { | 672 | static PySequenceMethods StaticTuple_as_sequence = { |
111 | 578 | (lenfunc)StaticTuple_length, /* sq_length */ | 673 | (lenfunc)StaticTuple_length, /* sq_length */ |
112 | 579 | 0, /* sq_concat */ | 674 | 0, /* sq_concat */ |
113 | @@ -604,7 +699,7 @@ | |||
114 | 604 | 0, /* tp_setattr */ | 699 | 0, /* tp_setattr */ |
115 | 605 | 0, /* tp_compare */ | 700 | 0, /* tp_compare */ |
116 | 606 | (reprfunc)StaticTuple_repr, /* tp_repr */ | 701 | (reprfunc)StaticTuple_repr, /* tp_repr */ |
118 | 607 | 0, /* tp_as_number */ | 702 | &StaticTuple_as_number, /* tp_as_number */ |
119 | 608 | &StaticTuple_as_sequence, /* tp_as_sequence */ | 703 | &StaticTuple_as_sequence, /* tp_as_sequence */ |
120 | 609 | 0, /* tp_as_mapping */ | 704 | 0, /* tp_as_mapping */ |
121 | 610 | (hashfunc)StaticTuple_hash, /* tp_hash */ | 705 | (hashfunc)StaticTuple_hash, /* tp_hash */ |
Preview Diff
1 | === modified file 'NEWS' | |||
2 | --- NEWS 2009-10-15 04:06:32 +0000 | |||
3 | +++ NEWS 2009-10-15 18:31:17 +0000 | |||
4 | @@ -25,6 +25,11 @@ | |||
5 | 25 | Improvements | 25 | Improvements |
6 | 26 | ************ | 26 | ************ |
7 | 27 | 27 | ||
8 | 28 | * When reading index files, we now use a ``StaticTuple`` rather than a | ||
9 | 29 | plain ``tuple`` object. This generally gives a 20% decrease in peak | ||
10 | 30 | memory, and can give a performance boost up to 40% on large projects. | ||
11 | 31 | (John Arbash Meinel) | ||
12 | 32 | |||
13 | 28 | Documentation | 33 | Documentation |
14 | 29 | ************* | 34 | ************* |
15 | 30 | 35 | ||
16 | @@ -45,13 +50,14 @@ | |||
17 | 45 | used as the interning structure for StaticTuple objects. | 50 | used as the interning structure for StaticTuple objects. |
18 | 46 | (John Arbash Meinel) | 51 | (John Arbash Meinel) |
19 | 47 | 52 | ||
27 | 48 | * ``bzrlib._static_tuple_pyx.StaticTuple`` is now available. This class | 53 | * ``bzrlib._static_tuple_pyx.StaticTuple`` is now available and used by |
28 | 49 | functions similarly to ``tuple`` objects. However, it can only point at | 54 | the btree index parser. This class functions similarly to ``tuple`` |
29 | 50 | other ``StaticTuple`` instances or strings. This allows us to remove it | 55 | objects. However, it can only point at other ``StaticTuple`` instances |
30 | 51 | from the garbage collector (it cannot be in a cycle), it also allows us | 56 | or strings. This allows us to remove it from the garbage collector (it |
31 | 52 | to intern the objects. In testing, this can reduce peak memory by | 57 | cannot be in a cycle), it also allows us to intern the objects. In |
32 | 53 | 20-40%, and significantly improve performance by removing objects from | 58 | testing, this can reduce peak memory by 20-40%, and significantly |
33 | 54 | being inspected by the garbage collector. (John Arbash Meinel) | 59 | improve performance by removing objects from being inspected by the |
34 | 60 | garbage collector. (John Arbash Meinel) | ||
35 | 55 | 61 | ||
36 | 56 | Testing | 62 | Testing |
37 | 57 | ******* | 63 | ******* |
38 | 58 | 64 | ||
39 | === modified file 'bzrlib/_bencode_pyx.pyx' | |||
40 | --- bzrlib/_bencode_pyx.pyx 2009-06-05 01:48:32 +0000 | |||
41 | +++ bzrlib/_bencode_pyx.pyx 2009-10-15 18:31:17 +0000 | |||
42 | @@ -58,6 +58,13 @@ | |||
43 | 58 | void D_UPDATE_TAIL(Decoder, int n) | 58 | void D_UPDATE_TAIL(Decoder, int n) |
44 | 59 | void E_UPDATE_TAIL(Encoder, int n) | 59 | void E_UPDATE_TAIL(Encoder, int n) |
45 | 60 | 60 | ||
46 | 61 | # To maintain compatibility with older versions of pyrex, we have to use the | ||
47 | 62 | # relative import here, rather than 'bzrlib._static_tuple_c' | ||
48 | 63 | from _static_tuple_c cimport StaticTuple, StaticTuple_CheckExact, \ | ||
49 | 64 | import_static_tuple_c | ||
50 | 65 | |||
51 | 66 | import_static_tuple_c() | ||
52 | 67 | |||
53 | 61 | 68 | ||
54 | 62 | cdef class Decoder: | 69 | cdef class Decoder: |
55 | 63 | """Bencode decoder""" | 70 | """Bencode decoder""" |
56 | @@ -371,7 +378,8 @@ | |||
57 | 371 | self._encode_int(x) | 378 | self._encode_int(x) |
58 | 372 | elif PyLong_CheckExact(x): | 379 | elif PyLong_CheckExact(x): |
59 | 373 | self._encode_long(x) | 380 | self._encode_long(x) |
61 | 374 | elif PyList_CheckExact(x) or PyTuple_CheckExact(x): | 381 | elif (PyList_CheckExact(x) or PyTuple_CheckExact(x) |
62 | 382 | or StaticTuple_CheckExact(x)): | ||
63 | 375 | self._encode_list(x) | 383 | self._encode_list(x) |
64 | 376 | elif PyDict_CheckExact(x): | 384 | elif PyDict_CheckExact(x): |
65 | 377 | self._encode_dict(x) | 385 | self._encode_dict(x) |
66 | 378 | 386 | ||
67 | === modified file 'bzrlib/_btree_serializer_pyx.pyx' | |||
68 | --- bzrlib/_btree_serializer_pyx.pyx 2009-10-08 05:12:01 +0000 | |||
69 | +++ bzrlib/_btree_serializer_pyx.pyx 2009-10-15 18:31:17 +0000 | |||
70 | @@ -38,6 +38,8 @@ | |||
71 | 38 | Py_ssize_t PyString_Size(object p) | 38 | Py_ssize_t PyString_Size(object p) |
72 | 39 | Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *) | 39 | Py_ssize_t PyString_GET_SIZE_ptr "PyString_GET_SIZE" (PyObject *) |
73 | 40 | char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *) | 40 | char * PyString_AS_STRING_ptr "PyString_AS_STRING" (PyObject *) |
74 | 41 | char * PyString_AS_STRING(object) | ||
75 | 42 | Py_ssize_t PyString_GET_SIZE(object) | ||
76 | 41 | int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len) | 43 | int PyString_AsStringAndSize_ptr(PyObject *, char **buf, Py_ssize_t *len) |
77 | 42 | void PyString_InternInPlace(PyObject **) | 44 | void PyString_InternInPlace(PyObject **) |
78 | 43 | int PyTuple_CheckExact(object t) | 45 | int PyTuple_CheckExact(object t) |
79 | @@ -55,6 +57,12 @@ | |||
80 | 55 | # void *memrchr(void *s, int c, size_t n) | 57 | # void *memrchr(void *s, int c, size_t n) |
81 | 56 | int strncmp(char *s1, char *s2, size_t n) | 58 | int strncmp(char *s1, char *s2, size_t n) |
82 | 57 | 59 | ||
83 | 60 | # It seems we need to import the definitions so that the pyrex compiler has | ||
84 | 61 | # local names to access them. | ||
85 | 62 | from _static_tuple_c cimport StaticTuple, \ | ||
86 | 63 | import_static_tuple_c, StaticTuple_New, \ | ||
87 | 64 | StaticTuple_Intern, StaticTuple_SET_ITEM, StaticTuple_CheckExact | ||
88 | 65 | |||
89 | 58 | 66 | ||
90 | 59 | # TODO: Find some way to import this from _dirstate_helpers | 67 | # TODO: Find some way to import this from _dirstate_helpers |
91 | 60 | cdef void* _my_memrchr(void *s, int c, size_t n): | 68 | cdef void* _my_memrchr(void *s, int c, size_t n): |
92 | @@ -71,6 +79,7 @@ | |||
93 | 71 | pos = pos - 1 | 79 | pos = pos - 1 |
94 | 72 | return NULL | 80 | return NULL |
95 | 73 | 81 | ||
96 | 82 | |||
97 | 74 | # TODO: Import this from _dirstate_helpers when it is merged | 83 | # TODO: Import this from _dirstate_helpers when it is merged |
98 | 75 | cdef object safe_string_from_size(char *s, Py_ssize_t size): | 84 | cdef object safe_string_from_size(char *s, Py_ssize_t size): |
99 | 76 | if size < 0: | 85 | if size < 0: |
100 | @@ -94,6 +103,10 @@ | |||
101 | 94 | Py_DECREF_ptr(py_str) | 103 | Py_DECREF_ptr(py_str) |
102 | 95 | return result | 104 | return result |
103 | 96 | 105 | ||
104 | 106 | from bzrlib import _static_tuple_c | ||
105 | 107 | # This sets up the StaticTuple C_API functionality | ||
106 | 108 | import_static_tuple_c() | ||
107 | 109 | |||
108 | 97 | 110 | ||
109 | 98 | cdef class BTreeLeafParser: | 111 | cdef class BTreeLeafParser: |
110 | 99 | """Parse the leaf nodes of a BTree index. | 112 | """Parse the leaf nodes of a BTree index. |
111 | @@ -133,6 +146,7 @@ | |||
112 | 133 | self._cur_str = NULL | 146 | self._cur_str = NULL |
113 | 134 | self._end_str = NULL | 147 | self._end_str = NULL |
114 | 135 | self._header_found = 0 | 148 | self._header_found = 0 |
115 | 149 | # keys are tuples | ||
116 | 136 | 150 | ||
117 | 137 | cdef extract_key(self, char * last): | 151 | cdef extract_key(self, char * last): |
118 | 138 | """Extract a key. | 152 | """Extract a key. |
119 | @@ -142,8 +156,9 @@ | |||
120 | 142 | """ | 156 | """ |
121 | 143 | cdef char *temp_ptr | 157 | cdef char *temp_ptr |
122 | 144 | cdef int loop_counter | 158 | cdef int loop_counter |
125 | 145 | # keys are tuples | 159 | cdef StaticTuple key |
126 | 146 | key = PyTuple_New(self.key_length) | 160 | |
127 | 161 | key = StaticTuple_New(self.key_length) | ||
128 | 147 | for loop_counter from 0 <= loop_counter < self.key_length: | 162 | for loop_counter from 0 <= loop_counter < self.key_length: |
129 | 148 | # grab a key segment | 163 | # grab a key segment |
130 | 149 | temp_ptr = <char*>memchr(self._start, c'\0', last - self._start) | 164 | temp_ptr = <char*>memchr(self._start, c'\0', last - self._start) |
131 | @@ -158,15 +173,19 @@ | |||
132 | 158 | last - self._start))) | 173 | last - self._start))) |
133 | 159 | raise AssertionError(failure_string) | 174 | raise AssertionError(failure_string) |
134 | 160 | # capture the key string | 175 | # capture the key string |
139 | 161 | # TODO: Consider using PyIntern_FromString, the only caveat is that | 176 | if (self.key_length == 1 |
140 | 162 | # it assumes a NULL-terminated string, so we have to check if | 177 | and (temp_ptr - self._start) == 45 |
141 | 163 | # temp_ptr[0] == c'\0' or some other char. | 178 | and strncmp(self._start, 'sha1:', 5) == 0): |
142 | 164 | key_element = safe_interned_string_from_size(self._start, | 179 | key_element = safe_string_from_size(self._start, |
143 | 180 | temp_ptr - self._start) | ||
144 | 181 | else: | ||
145 | 182 | key_element = safe_interned_string_from_size(self._start, | ||
146 | 165 | temp_ptr - self._start) | 183 | temp_ptr - self._start) |
147 | 166 | # advance our pointer | 184 | # advance our pointer |
148 | 167 | self._start = temp_ptr + 1 | 185 | self._start = temp_ptr + 1 |
149 | 168 | Py_INCREF(key_element) | 186 | Py_INCREF(key_element) |
151 | 169 | PyTuple_SET_ITEM(key, loop_counter, key_element) | 187 | StaticTuple_SET_ITEM(key, loop_counter, key_element) |
152 | 188 | key = StaticTuple_Intern(key) | ||
153 | 170 | return key | 189 | return key |
154 | 171 | 190 | ||
155 | 172 | cdef int process_line(self) except -1: | 191 | cdef int process_line(self) except -1: |
156 | @@ -176,6 +195,7 @@ | |||
157 | 176 | cdef char *ref_ptr | 195 | cdef char *ref_ptr |
158 | 177 | cdef char *next_start | 196 | cdef char *next_start |
159 | 178 | cdef int loop_counter | 197 | cdef int loop_counter |
160 | 198 | cdef Py_ssize_t str_len | ||
161 | 179 | 199 | ||
162 | 180 | self._start = self._cur_str | 200 | self._start = self._cur_str |
163 | 181 | # Find the next newline | 201 | # Find the next newline |
164 | @@ -211,12 +231,25 @@ | |||
165 | 211 | # Invalid line | 231 | # Invalid line |
166 | 212 | raise AssertionError("Failed to find the value area") | 232 | raise AssertionError("Failed to find the value area") |
167 | 213 | else: | 233 | else: |
170 | 214 | # capture the value string | 234 | # Because of how conversions were done, we ended up with *lots* of |
171 | 215 | value = safe_string_from_size(temp_ptr + 1, last - temp_ptr - 1) | 235 | # values that are identical. These are all of the 0-length nodes |
172 | 236 | # that are referred to by the TREE_ROOT (and likely some other | ||
173 | 237 | # directory nodes.) For example, bzr has 25k references to | ||
174 | 238 | # something like '12607215 328306 0 0', which ends up consuming 1MB | ||
175 | 239 | # of memory, just for those strings. | ||
176 | 240 | str_len = last - temp_ptr - 1 | ||
177 | 241 | if (str_len > 4 | ||
178 | 242 | and strncmp(" 0 0", last - 4, 4) == 0): | ||
179 | 243 | # This drops peak mem for bzr.dev from 87.4MB => 86.2MB | ||
180 | 244 | # For Launchpad 236MB => 232MB | ||
181 | 245 | value = safe_interned_string_from_size(temp_ptr + 1, str_len) | ||
182 | 246 | else: | ||
183 | 247 | value = safe_string_from_size(temp_ptr + 1, str_len) | ||
184 | 216 | # shrink the references end point | 248 | # shrink the references end point |
185 | 217 | last = temp_ptr | 249 | last = temp_ptr |
186 | 250 | |||
187 | 218 | if self.ref_list_length: | 251 | if self.ref_list_length: |
189 | 219 | ref_lists = [] | 252 | ref_lists = StaticTuple_New(self.ref_list_length) |
190 | 220 | loop_counter = 0 | 253 | loop_counter = 0 |
191 | 221 | while loop_counter < self.ref_list_length: | 254 | while loop_counter < self.ref_list_length: |
192 | 222 | ref_list = [] | 255 | ref_list = [] |
193 | @@ -248,18 +281,20 @@ | |||
194 | 248 | if temp_ptr == NULL: | 281 | if temp_ptr == NULL: |
195 | 249 | # key runs to the end | 282 | # key runs to the end |
196 | 250 | temp_ptr = ref_ptr | 283 | temp_ptr = ref_ptr |
197 | 284 | |||
198 | 251 | PyList_Append(ref_list, self.extract_key(temp_ptr)) | 285 | PyList_Append(ref_list, self.extract_key(temp_ptr)) |
200 | 252 | PyList_Append(ref_lists, tuple(ref_list)) | 286 | ref_list = StaticTuple_Intern(StaticTuple(*ref_list)) |
201 | 287 | Py_INCREF(ref_list) | ||
202 | 288 | StaticTuple_SET_ITEM(ref_lists, loop_counter - 1, ref_list) | ||
203 | 253 | # prepare for the next reference list | 289 | # prepare for the next reference list |
204 | 254 | self._start = next_start | 290 | self._start = next_start |
207 | 255 | ref_lists = tuple(ref_lists) | 291 | node_value = StaticTuple(value, ref_lists) |
206 | 256 | node_value = (value, ref_lists) | ||
208 | 257 | else: | 292 | else: |
209 | 258 | if last != self._start: | 293 | if last != self._start: |
210 | 259 | # unexpected reference data present | 294 | # unexpected reference data present |
211 | 260 | raise AssertionError("unexpected reference data present") | 295 | raise AssertionError("unexpected reference data present") |
214 | 261 | node_value = (value, ()) | 296 | node_value = StaticTuple(value, StaticTuple()) |
215 | 262 | PyList_Append(self.keys, (key, node_value)) | 297 | PyList_Append(self.keys, StaticTuple(key, node_value)) |
216 | 263 | return 0 | 298 | return 0 |
217 | 264 | 299 | ||
218 | 265 | def parse(self): | 300 | def parse(self): |
219 | @@ -294,7 +329,6 @@ | |||
220 | 294 | cdef Py_ssize_t flat_len | 329 | cdef Py_ssize_t flat_len |
221 | 295 | cdef Py_ssize_t key_len | 330 | cdef Py_ssize_t key_len |
222 | 296 | cdef Py_ssize_t node_len | 331 | cdef Py_ssize_t node_len |
223 | 297 | cdef PyObject * val | ||
224 | 298 | cdef char * value | 332 | cdef char * value |
225 | 299 | cdef Py_ssize_t value_len | 333 | cdef Py_ssize_t value_len |
226 | 300 | cdef char * out | 334 | cdef char * out |
227 | @@ -303,13 +337,12 @@ | |||
228 | 303 | cdef int first_ref_list | 337 | cdef int first_ref_list |
229 | 304 | cdef int first_reference | 338 | cdef int first_reference |
230 | 305 | cdef int i | 339 | cdef int i |
231 | 306 | cdef PyObject *ref_bit | ||
232 | 307 | cdef Py_ssize_t ref_bit_len | 340 | cdef Py_ssize_t ref_bit_len |
233 | 308 | 341 | ||
236 | 309 | if not PyTuple_CheckExact(node): | 342 | if not PyTuple_CheckExact(node) and not StaticTuple_CheckExact(node): |
237 | 310 | raise TypeError('We expected a tuple() for node not: %s' | 343 | raise TypeError('We expected a tuple() or StaticTuple() for node not: %s' |
238 | 311 | % type(node)) | 344 | % type(node)) |
240 | 312 | node_len = PyTuple_GET_SIZE(node) | 345 | node_len = len(node) |
241 | 313 | have_reference_lists = reference_lists | 346 | have_reference_lists = reference_lists |
242 | 314 | if have_reference_lists: | 347 | if have_reference_lists: |
243 | 315 | if node_len != 4: | 348 | if node_len != 4: |
244 | @@ -318,8 +351,17 @@ | |||
245 | 318 | elif node_len < 3: | 351 | elif node_len < 3: |
246 | 319 | raise ValueError('Without ref_lists, we need at least 3 entries not: %s' | 352 | raise ValueError('Without ref_lists, we need at least 3 entries not: %s' |
247 | 320 | % len(node)) | 353 | % len(node)) |
250 | 321 | # I don't expect that we can do faster than string.join() | 354 | # TODO: We can probably do better than string.join(), namely |
251 | 322 | string_key = '\0'.join(<object>PyTuple_GET_ITEM_ptr_object(node, 1)) | 355 | # when key has only 1 item, we can just grab that string |
252 | 356 | # And when there are 2 items, we could do a single malloc + len() + 1 | ||
253 | 357 | # also, doing .join() requires a PyObject_GetAttrString call, which | ||
254 | 358 | # we could also avoid. | ||
255 | 359 | # TODO: Note that pyrex 0.9.6 generates fairly crummy code here, using the | ||
256 | 360 | # python object interface, versus 0.9.8+ which uses a helper that | ||
257 | 361 | # checks if this supports the sequence interface. | ||
258 | 362 | # We *could* do more work on our own, and grab the actual items | ||
259 | 363 | # lists. For now, just ask people to use a better compiler. :) | ||
260 | 364 | string_key = '\0'.join(node[1]) | ||
261 | 323 | 365 | ||
262 | 324 | # TODO: instead of using string joins, precompute the final string length, | 366 | # TODO: instead of using string joins, precompute the final string length, |
263 | 325 | # and then malloc a single string and copy everything in. | 367 | # and then malloc a single string and copy everything in. |
264 | @@ -336,7 +378,7 @@ | |||
265 | 336 | refs_len = 0 | 378 | refs_len = 0 |
266 | 337 | if have_reference_lists: | 379 | if have_reference_lists: |
267 | 338 | # Figure out how many bytes it will take to store the references | 380 | # Figure out how many bytes it will take to store the references |
269 | 339 | ref_lists = <object>PyTuple_GET_ITEM_ptr_object(node, 3) | 381 | ref_lists = node[3] |
270 | 340 | next_len = len(ref_lists) # TODO: use a Py function | 382 | next_len = len(ref_lists) # TODO: use a Py function |
271 | 341 | if next_len > 0: | 383 | if next_len > 0: |
272 | 342 | # If there are no nodes, we don't need to do any work | 384 | # If there are no nodes, we don't need to do any work |
273 | @@ -350,31 +392,31 @@ | |||
274 | 350 | # references | 392 | # references |
275 | 351 | refs_len = refs_len + (next_len - 1) | 393 | refs_len = refs_len + (next_len - 1) |
276 | 352 | for reference in ref_list: | 394 | for reference in ref_list: |
278 | 353 | if not PyTuple_CheckExact(reference): | 395 | if (not PyTuple_CheckExact(reference) |
279 | 396 | and not StaticTuple_CheckExact(reference)): | ||
280 | 354 | raise TypeError( | 397 | raise TypeError( |
281 | 355 | 'We expect references to be tuples not: %s' | 398 | 'We expect references to be tuples not: %s' |
282 | 356 | % type(reference)) | 399 | % type(reference)) |
284 | 357 | next_len = PyTuple_GET_SIZE(reference) | 400 | next_len = len(reference) |
285 | 358 | if next_len > 0: | 401 | if next_len > 0: |
286 | 359 | # We will need (len - 1) '\x00' characters to | 402 | # We will need (len - 1) '\x00' characters to |
287 | 360 | # separate the reference key | 403 | # separate the reference key |
288 | 361 | refs_len = refs_len + (next_len - 1) | 404 | refs_len = refs_len + (next_len - 1) |
292 | 362 | for i from 0 <= i < next_len: | 405 | for ref_bit in reference: |
293 | 363 | ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i) | 406 | if not PyString_CheckExact(ref_bit): |
291 | 364 | if not PyString_CheckExact_ptr(ref_bit): | ||
294 | 365 | raise TypeError('We expect reference bits' | 407 | raise TypeError('We expect reference bits' |
295 | 366 | ' to be strings not: %s' | 408 | ' to be strings not: %s' |
296 | 367 | % type(<object>ref_bit)) | 409 | % type(<object>ref_bit)) |
298 | 368 | refs_len = refs_len + PyString_GET_SIZE_ptr(ref_bit) | 410 | refs_len = refs_len + PyString_GET_SIZE(ref_bit) |
299 | 369 | 411 | ||
300 | 370 | # So we have the (key NULL refs NULL value LF) | 412 | # So we have the (key NULL refs NULL value LF) |
301 | 371 | key_len = PyString_Size(string_key) | 413 | key_len = PyString_Size(string_key) |
304 | 372 | val = PyTuple_GET_ITEM_ptr_object(node, 2) | 414 | val = node[2] |
305 | 373 | if not PyString_CheckExact_ptr(val): | 415 | if not PyString_CheckExact(val): |
306 | 374 | raise TypeError('Expected a plain str for value not: %s' | 416 | raise TypeError('Expected a plain str for value not: %s' |
310 | 375 | % type(<object>val)) | 417 | % type(val)) |
311 | 376 | value = PyString_AS_STRING_ptr(val) | 418 | value = PyString_AS_STRING(val) |
312 | 377 | value_len = PyString_GET_SIZE_ptr(val) | 419 | value_len = PyString_GET_SIZE(val) |
313 | 378 | flat_len = (key_len + 1 + refs_len + 1 + value_len + 1) | 420 | flat_len = (key_len + 1 + refs_len + 1 + value_len + 1) |
314 | 379 | line = PyString_FromStringAndSize(NULL, flat_len) | 421 | line = PyString_FromStringAndSize(NULL, flat_len) |
315 | 380 | # Get a pointer to the new buffer | 422 | # Get a pointer to the new buffer |
316 | @@ -396,14 +438,14 @@ | |||
317 | 396 | out[0] = c'\r' | 438 | out[0] = c'\r' |
318 | 397 | out = out + 1 | 439 | out = out + 1 |
319 | 398 | first_reference = 0 | 440 | first_reference = 0 |
321 | 399 | next_len = PyTuple_GET_SIZE(reference) | 441 | next_len = len(reference) |
322 | 400 | for i from 0 <= i < next_len: | 442 | for i from 0 <= i < next_len: |
323 | 401 | if i != 0: | 443 | if i != 0: |
324 | 402 | out[0] = c'\x00' | 444 | out[0] = c'\x00' |
325 | 403 | out = out + 1 | 445 | out = out + 1 |
329 | 404 | ref_bit = PyTuple_GET_ITEM_ptr_object(reference, i) | 446 | ref_bit = reference[i] |
330 | 405 | ref_bit_len = PyString_GET_SIZE_ptr(ref_bit) | 447 | ref_bit_len = PyString_GET_SIZE(ref_bit) |
331 | 406 | memcpy(out, PyString_AS_STRING_ptr(ref_bit), ref_bit_len) | 448 | memcpy(out, PyString_AS_STRING(ref_bit), ref_bit_len) |
332 | 407 | out = out + ref_bit_len | 449 | out = out + ref_bit_len |
333 | 408 | out[0] = c'\0' | 450 | out[0] = c'\0' |
334 | 409 | out = out + 1 | 451 | out = out + 1 |
335 | 410 | 452 | ||
336 | === modified file 'bzrlib/_static_tuple_c.c' | |||
337 | --- bzrlib/_static_tuple_c.c 2009-10-15 16:18:47 +0000 | |||
338 | +++ bzrlib/_static_tuple_c.c 2009-10-15 18:31:17 +0000 | |||
339 | @@ -418,9 +418,15 @@ | |||
340 | 418 | return NULL; /* There seems to be an error */ | 418 | return NULL; /* There seems to be an error */ |
341 | 419 | } | 419 | } |
342 | 420 | if (result == Py_NotImplemented) { | 420 | if (result == Py_NotImplemented) { |
343 | 421 | PyErr_BadInternalCall(); | ||
344 | 422 | Py_DECREF(result); | 421 | Py_DECREF(result); |
346 | 423 | return NULL; | 422 | /* One side must have had a string and the other a StaticTuple. |
347 | 423 | * This clearly means that they are not equal. | ||
348 | 424 | */ | ||
349 | 425 | if (op == Py_EQ) { | ||
350 | 426 | Py_INCREF(Py_False); | ||
351 | 427 | return Py_False; | ||
352 | 428 | } | ||
353 | 429 | result = PyObject_RichCompare(v_obj, w_obj, Py_EQ); | ||
354 | 424 | } | 430 | } |
355 | 425 | if (result == Py_False) { | 431 | if (result == Py_False) { |
356 | 426 | /* This entry is not identical | 432 | /* This entry is not identical |
357 | 427 | 433 | ||
358 | === modified file 'bzrlib/btree_index.py' | |||
359 | --- bzrlib/btree_index.py 2009-10-15 04:01:26 +0000 | |||
360 | +++ bzrlib/btree_index.py 2009-10-15 18:31:17 +0000 | |||
361 | @@ -163,6 +163,7 @@ | |||
362 | 163 | node_refs, _ = self._check_key_ref_value(key, references, value) | 163 | node_refs, _ = self._check_key_ref_value(key, references, value) |
363 | 164 | if key in self._nodes: | 164 | if key in self._nodes: |
364 | 165 | raise errors.BadIndexDuplicateKey(key, self) | 165 | raise errors.BadIndexDuplicateKey(key, self) |
365 | 166 | # TODO: StaticTuple | ||
366 | 166 | self._nodes[key] = (node_refs, value) | 167 | self._nodes[key] = (node_refs, value) |
367 | 167 | self._keys.add(key) | 168 | self._keys.add(key) |
368 | 168 | if self._nodes_by_key is not None and self._key_length > 1: | 169 | if self._nodes_by_key is not None and self._key_length > 1: |
369 | @@ -625,6 +626,7 @@ | |||
370 | 625 | for line in lines[2:]: | 626 | for line in lines[2:]: |
371 | 626 | if line == '': | 627 | if line == '': |
372 | 627 | break | 628 | break |
373 | 629 | # TODO: Switch to StaticTuple here. | ||
374 | 628 | nodes.append(tuple(map(intern, line.split('\0')))) | 630 | nodes.append(tuple(map(intern, line.split('\0')))) |
375 | 629 | return nodes | 631 | return nodes |
376 | 630 | 632 | ||
377 | 631 | 633 | ||
378 | === modified file 'bzrlib/builtins.py' | |||
379 | --- bzrlib/builtins.py 2009-10-08 16:32:43 +0000 | |||
380 | +++ bzrlib/builtins.py 2009-10-15 18:31:17 +0000 | |||
381 | @@ -431,7 +431,10 @@ | |||
382 | 431 | for node in bt.iter_all_entries(): | 431 | for node in bt.iter_all_entries(): |
383 | 432 | # Node is made up of: | 432 | # Node is made up of: |
384 | 433 | # (index, key, value, [references]) | 433 | # (index, key, value, [references]) |
386 | 434 | self.outf.write('%s\n' % (node[1:],)) | 434 | refs_as_tuples = tuple([tuple([tuple(ref) for ref in ref_list]) |
387 | 435 | for ref_list in node[3]]) | ||
388 | 436 | as_tuple = (tuple(node[1]), node[2], refs_as_tuples) | ||
389 | 437 | self.outf.write('%s\n' % (as_tuple,)) | ||
390 | 435 | 438 | ||
391 | 436 | 439 | ||
392 | 437 | class cmd_remove_tree(Command): | 440 | class cmd_remove_tree(Command): |
393 | 438 | 441 | ||
394 | === modified file 'bzrlib/index.py' | |||
395 | --- bzrlib/index.py 2009-10-13 05:20:50 +0000 | |||
396 | +++ bzrlib/index.py 2009-10-15 18:31:17 +0000 | |||
397 | @@ -40,6 +40,7 @@ | |||
398 | 40 | debug, | 40 | debug, |
399 | 41 | errors, | 41 | errors, |
400 | 42 | ) | 42 | ) |
401 | 43 | from bzrlib.static_tuple import StaticTuple | ||
402 | 43 | 44 | ||
403 | 44 | _HEADER_READV = (0, 200) | 45 | _HEADER_READV = (0, 200) |
404 | 45 | _OPTION_KEY_ELEMENTS = "key_elements=" | 46 | _OPTION_KEY_ELEMENTS = "key_elements=" |
405 | @@ -102,7 +103,7 @@ | |||
406 | 102 | 103 | ||
407 | 103 | def _check_key(self, key): | 104 | def _check_key(self, key): |
408 | 104 | """Raise BadIndexKey if key is not a valid key for this index.""" | 105 | """Raise BadIndexKey if key is not a valid key for this index.""" |
410 | 105 | if type(key) != tuple: | 106 | if type(key) not in (tuple, StaticTuple): |
411 | 106 | raise errors.BadIndexKey(key) | 107 | raise errors.BadIndexKey(key) |
412 | 107 | if self._key_length != len(key): | 108 | if self._key_length != len(key): |
413 | 108 | raise errors.BadIndexKey(key) | 109 | raise errors.BadIndexKey(key) |
414 | @@ -202,7 +203,9 @@ | |||
415 | 202 | if reference not in self._nodes: | 203 | if reference not in self._nodes: |
416 | 203 | self._check_key(reference) | 204 | self._check_key(reference) |
417 | 204 | absent_references.append(reference) | 205 | absent_references.append(reference) |
418 | 206 | # TODO: StaticTuple | ||
419 | 205 | node_refs.append(tuple(reference_list)) | 207 | node_refs.append(tuple(reference_list)) |
420 | 208 | # TODO: StaticTuple | ||
421 | 206 | return tuple(node_refs), absent_references | 209 | return tuple(node_refs), absent_references |
422 | 207 | 210 | ||
423 | 208 | def add_node(self, key, value, references=()): | 211 | def add_node(self, key, value, references=()): |
424 | 209 | 212 | ||
425 | === modified file 'bzrlib/repository.py' | |||
426 | --- bzrlib/repository.py 2009-10-08 22:53:13 +0000 | |||
427 | +++ bzrlib/repository.py 2009-10-15 18:31:17 +0000 | |||
428 | @@ -4319,6 +4319,13 @@ | |||
429 | 4319 | ): | 4319 | ): |
430 | 4320 | if versioned_file is None: | 4320 | if versioned_file is None: |
431 | 4321 | continue | 4321 | continue |
432 | 4322 | # TODO: key is often going to be a StaticTuple object | ||
433 | 4323 | # I don't believe we can define a method by which | ||
434 | 4324 | # (prefix,) + StaticTuple will work, though we could | ||
435 | 4325 | # define a StaticTuple.sq_concat that would allow you to | ||
436 | 4326 | # pass in either a tuple or a StaticTuple as the second | ||
437 | 4327 | # object, so instead we could have: | ||
438 | 4328 | # StaticTuple(prefix) + key here... | ||
439 | 4322 | missing_keys.update((prefix,) + key for key in | 4329 | missing_keys.update((prefix,) + key for key in |
440 | 4323 | versioned_file.get_missing_compression_parent_keys()) | 4330 | versioned_file.get_missing_compression_parent_keys()) |
441 | 4324 | except NotImplementedError: | 4331 | except NotImplementedError: |
442 | 4325 | 4332 | ||
443 | === added file 'bzrlib/static_tuple.py' | |||
444 | --- bzrlib/static_tuple.py 1970-01-01 00:00:00 +0000 | |||
445 | +++ bzrlib/static_tuple.py 2009-10-15 18:31:17 +0000 | |||
446 | @@ -0,0 +1,25 @@ | |||
447 | 1 | # Copyright (C) 2009 Canonical Ltd | ||
448 | 2 | # | ||
449 | 3 | # This program is free software; you can redistribute it and/or modify | ||
450 | 4 | # it under the terms of the GNU General Public License as published by | ||
451 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
452 | 6 | # (at your option) any later version. | ||
453 | 7 | # | ||
454 | 8 | # This program is distributed in the hope that it will be useful, | ||
455 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
456 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
457 | 11 | # GNU General Public License for more details. | ||
458 | 12 | # | ||
459 | 13 | # You should have received a copy of the GNU General Public License | ||
460 | 14 | # along with this program; if not, write to the Free Software | ||
461 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
462 | 16 | |||
463 | 17 | """Interface thunk for a StaticTuple implementation.""" | ||
464 | 18 | |||
465 | 19 | try: | ||
466 | 20 | from bzrlib._static_tuple_c import StaticTuple | ||
467 | 21 | except ImportError, e: | ||
468 | 22 | from bzrlib import osutils | ||
469 | 23 | osutils.failed_to_load_extension(e) | ||
470 | 24 | from bzrlib._static_tuple_py import StaticTuple | ||
471 | 25 | |||
472 | 0 | 26 | ||
473 | === modified file 'bzrlib/tests/test__static_tuple.py' | |||
474 | --- bzrlib/tests/test__static_tuple.py 2009-10-12 18:10:24 +0000 | |||
475 | +++ bzrlib/tests/test__static_tuple.py 2009-10-15 18:31:17 +0000 | |||
476 | @@ -23,6 +23,7 @@ | |||
477 | 23 | _static_tuple_py, | 23 | _static_tuple_py, |
478 | 24 | errors, | 24 | errors, |
479 | 25 | osutils, | 25 | osutils, |
480 | 26 | static_tuple, | ||
481 | 26 | tests, | 27 | tests, |
482 | 27 | ) | 28 | ) |
483 | 28 | 29 | ||
484 | @@ -278,6 +279,16 @@ | |||
485 | 278 | self.assertCompareEqual(k3, (k1, ('foo', 'bar'))) | 279 | self.assertCompareEqual(k3, (k1, ('foo', 'bar'))) |
486 | 279 | self.assertCompareEqual((k1, ('foo', 'bar')), k3) | 280 | self.assertCompareEqual((k1, ('foo', 'bar')), k3) |
487 | 280 | 281 | ||
488 | 282 | def test_compare_mixed_depths(self): | ||
489 | 283 | stuple = self.module.StaticTuple | ||
490 | 284 | k1 = stuple(stuple('a',), stuple('b',)) | ||
491 | 285 | k2 = stuple(stuple(stuple('c',), stuple('d',)), | ||
492 | 286 | stuple('b',)) | ||
493 | 287 | # This requires comparing a StaticTuple to a 'string', and then | ||
494 | 288 | # interpreting that value in the next higher StaticTuple. This used to | ||
495 | 289 | # generate a PyErr_BadIternalCall. We now fall back to *something*. | ||
496 | 290 | self.assertCompareNoRelation(k1, k2) | ||
497 | 291 | |||
498 | 281 | def test_hash(self): | 292 | def test_hash(self): |
499 | 282 | k = self.module.StaticTuple('foo') | 293 | k = self.module.StaticTuple('foo') |
500 | 283 | self.assertEqual(hash(k), hash(('foo',))) | 294 | self.assertEqual(hash(k), hash(('foo',))) |
501 | @@ -416,3 +427,13 @@ | |||
502 | 416 | if self.module is _static_tuple_py: | 427 | if self.module is _static_tuple_py: |
503 | 417 | return | 428 | return |
504 | 418 | self.assertIsNot(None, self.module._C_API) | 429 | self.assertIsNot(None, self.module._C_API) |
505 | 430 | |||
506 | 431 | def test_static_tuple_thunk(self): | ||
507 | 432 | # Make sure the right implementation is available from | ||
508 | 433 | # bzrlib.static_tuple.StaticTuple. | ||
509 | 434 | if self.module is _static_tuple_py: | ||
510 | 435 | if CompiledStaticTuple.available(): | ||
511 | 436 | # We will be using the C version | ||
512 | 437 | return | ||
513 | 438 | self.assertIs(static_tuple.StaticTuple, | ||
514 | 439 | self.module.StaticTuple) | ||
515 | 419 | 440 | ||
516 | === modified file 'bzrlib/util/_bencode_py.py' | |||
517 | --- bzrlib/util/_bencode_py.py 2009-06-10 03:56:49 +0000 | |||
518 | +++ bzrlib/util/_bencode_py.py 2009-10-15 18:31:17 +0000 | |||
519 | @@ -154,6 +154,13 @@ | |||
520 | 154 | encode_int(int(x), r) | 154 | encode_int(int(x), r) |
521 | 155 | encode_func[BooleanType] = encode_bool | 155 | encode_func[BooleanType] = encode_bool |
522 | 156 | 156 | ||
523 | 157 | try: | ||
524 | 158 | from bzrlib._static_tuple_c import StaticTuple | ||
525 | 159 | except ImportError: | ||
526 | 160 | pass | ||
527 | 161 | else: | ||
528 | 162 | encode_func[StaticTuple] = encode_list | ||
529 | 163 | |||
530 | 157 | 164 | ||
531 | 158 | def bencode(x): | 165 | def bencode(x): |
532 | 159 | r = [] | 166 | r = [] |
533 | 160 | 167 | ||
534 | === modified file 'setup.py' | |||
535 | --- setup.py 2009-10-12 17:03:40 +0000 | |||
536 | +++ setup.py 2009-10-15 18:31:17 +0000 | |||
537 | @@ -270,7 +270,6 @@ | |||
538 | 270 | 270 | ||
539 | 271 | add_pyrex_extension('bzrlib._annotator_pyx') | 271 | add_pyrex_extension('bzrlib._annotator_pyx') |
540 | 272 | add_pyrex_extension('bzrlib._bencode_pyx') | 272 | add_pyrex_extension('bzrlib._bencode_pyx') |
541 | 273 | add_pyrex_extension('bzrlib._btree_serializer_pyx') | ||
542 | 274 | add_pyrex_extension('bzrlib._chunks_to_lines_pyx') | 273 | add_pyrex_extension('bzrlib._chunks_to_lines_pyx') |
543 | 275 | add_pyrex_extension('bzrlib._groupcompress_pyx', | 274 | add_pyrex_extension('bzrlib._groupcompress_pyx', |
544 | 276 | extra_source=['bzrlib/diff-delta.c']) | 275 | extra_source=['bzrlib/diff-delta.c']) |
545 | @@ -303,6 +302,8 @@ | |||
546 | 303 | add_pyrex_extension('bzrlib._simple_set_pyx') | 302 | add_pyrex_extension('bzrlib._simple_set_pyx') |
547 | 304 | ext_modules.append(Extension('bzrlib._static_tuple_c', | 303 | ext_modules.append(Extension('bzrlib._static_tuple_c', |
548 | 305 | ['bzrlib/_static_tuple_c.c'])) | 304 | ['bzrlib/_static_tuple_c.c'])) |
549 | 305 | add_pyrex_extension('bzrlib._btree_serializer_pyx') | ||
550 | 306 | |||
551 | 306 | 307 | ||
552 | 307 | if unavailable_files: | 308 | if unavailable_files: |
553 | 308 | print 'C extension(s) not found:' | 309 | print 'C extension(s) not found:' |
This is the same as an earlier patch for using StaticTuple as part of the btree code. It has a couple small additions.
1) Small fix for 'bzr dump-btree' that casts the objects back to tuples for nicer formatting. as_tuples= True' to return StaticTuple
2) Add 'StaticTuple' as a type that 'bencode' knows how to deal with (just treats it as another
Tuple/List object.)
Arguably we probably want to end up with 'decode_
instances. For now, though, this was all that was necessary to get the test suite to pass on my
machine. (Though a lot of the tests that were failing on PQM weren't failing here anyway...)