Rev 4695: Initial work for a Key class. in http://bazaar.launchpad.net/~jameinel/bzr/2.1-memory-consumption
John Arbash Meinel
john at arbash-meinel.com
Thu Sep 10 19:15:33 BST 2009
At http://bazaar.launchpad.net/~jameinel/bzr/2.1-memory-consumption
------------------------------------------------------------
revno: 4695
revision-id: john at arbash-meinel.com-20090910181517-hby4nv0dus37pavn
parent: john at arbash-meinel.com-20090910172810-9zxjgnx54q1epk7d
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.1-memory-consumption
timestamp: Thu 2009-09-10 13:15:17 -0500
message:
Initial work for a Key class.
-------------- next part --------------
=== modified file 'bzrlib/_keys_type_c.c'
--- a/bzrlib/_keys_type_c.c 2009-09-10 17:28:10 +0000
+++ b/bzrlib/_keys_type_c.c 2009-09-10 18:15:17 +0000
@@ -28,6 +28,21 @@
#endif
+/* This defines a single variable-width key.
+ * It is basically the same as a tuple, but
+ * 1) Lighter weight in memory
+ * 2) Only supports strings.
+ * It is mostly used as a helper. Note that Keys() is a similar structure for
+ * lists of Key objects. Its main advantage, though, is that it inlines all of
+ * the Key objects so that you have 1 python object overhead for N Keys, rather
+ * than N objects.
+ */
+typedef struct {
+ PyObject_VAR_HEAD
+ PyStringObject *key_strings[1];
+} Key;
+extern PyTypeObject KeyType;
+
/* Because of object alignment, it seems that using unsigned char doesn't make
* things any smaller than using an 'int'... :(
* Perhaps we should use the high bits for extra flags?
@@ -45,6 +60,154 @@
extern PyTypeObject KeysType;
static PyObject *Keys_item(Keys *self, Py_ssize_t offset);
+
+static PyObject *
+Key_as_tuple(Key *self)
+{
+ PyObject *tpl = NULL, *obj = NULL;
+ Py_ssize_t i;
+
+ tpl = PyTuple_New(self->ob_size);
+ if (!tpl) {
+ /* Malloc failure */
+ return NULL;
+ }
+ for (i = 0; i < self->ob_size; ++i) {
+ obj = (PyObject *)self->key_strings[i];
+ Py_INCREF(obj);
+ PyTuple_SET_ITEM(tpl, i, obj);
+ }
+ return tpl;
+}
+
+static char Key_as_tuple_doc[] = "as_tuple() => tuple";
+
+static void
+Key_dealloc(Keys *self)
+{
+ // /* Do we want to use the Py_TRASHCAN_SAFE_BEGIN/END operations? */
+ // if (num_keys > 0) {
+ // /* tuple deallocs from the end to the beginning. Not sure why, but
+ // * we'll do the same here.
+ // */
+ // int i;
+ // for(i = num_keys - 1; i >= 0; --i) {
+ // Py_XDECREF(self->key_strings[i]);
+ // }
+ // }
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+
+static PyObject *
+Key_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ Key *self;
+ PyObject *obj = NULL;
+ Py_ssize_t i, len = 0;
+
+ if (type != &KeyType) {
+ PyErr_SetString(PyExc_TypeError, "we only support creating Key");
+ return NULL;
+ }
+ if (!PyTuple_CheckExact(args)) {
+ PyErr_SetString(PyExc_TypeError, "args must be a tuple");
+ return NULL;
+ }
+ len = PyTuple_GET_SIZE(args);
+ if (len <= 0 || len > 256) {
+ /* Too big or too small */
+ PyErr_SetString(PyExc_ValueError, "Key.__init__(...)"
+ " takes from 1 to 256 key bits");
+ return NULL;
+ }
+ self = (Key *)(type->tp_alloc(type, len));
+ self->ob_size = len;
+ for (i = 0; i < len; ++i) {
+ obj = PyTuple_GET_ITEM(args, i);
+ if (!PyString_CheckExact(obj)) {
+ PyErr_SetString(PyExc_TypeError, "Key.__init__(...)"
+ " requires that all key bits are strings.");
+ /* TODO: What is the proper way to dealloc ? */
+ type->tp_dealloc((PyObject *)self);
+ return NULL;
+ }
+ Py_INCREF(obj);
+ self->key_strings[i] = (PyStringObject *)obj;
+ }
+ return (PyObject *)self;
+}
+
+
+static char Key_doc[] =
+ "C implementation of a Key structure."
+ "\n This is used as Key(key_bit_1, key_bit_2, key_bit_3, ...)"
+ "\n This is similar to tuple, just less flexible in what it"
+ "\n supports, but also lighter memory consumption.";
+
+static PyMethodDef Key_methods[] = {
+ {"as_tuple", (PyCFunction)Key_as_tuple, METH_NOARGS, Key_as_tuple_doc},
+ {NULL, NULL} /* sentinel */
+};
+
+// static PySequenceMethods Key_as_sequence = {
+// (lenfunc)Key_length, /* sq_length */
+// 0, /* sq_concat */
+// 0, /* sq_repeat */
+// (ssizeargfunc)Key_item, /* sq_item */
+// 0, /* sq_slice */
+// 0, /* sq_ass_item */
+// 0, /* sq_ass_slice */
+// 0, /* sq_contains */
+// };
+
+static PyTypeObject KeyType = {
+ PyObject_HEAD_INIT(NULL)
+ 0, /* ob_size */
+ "Key", /* tp_name */
+ sizeof(Key) - sizeof(PyStringObject *), /* tp_basicsize */
+ sizeof(PyObject *), /* tp_itemsize */
+ Key_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, //(reprfunc)Keys_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, //&Keys_as_sequence, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, //(hashfunc)Keys_hash, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags*/
+ Key_doc, /* tp_doc */
+ // might want to set this, except we don't participate in gc, so it might
+ // confuse things
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ // TODO: implement richcompare, we should probably be able to compare vs an
+ // tuple, as well as versus another Keys object.
+ 0, //Key_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ // We could implement this as returning tuples of keys...
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Key_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Key_new, /* tp_new */
+};
+
static inline void
Keys_set_info(Keys *self, int key_width,
int num_keys, int flags)
@@ -102,12 +265,11 @@
long num_keys;
long num_key_bits;
long flags = 0; /* Not used */
- PyObject *obj= NULL;
+ PyObject *obj= NULL;
Keys *self;
- if (type != &KeysType) {
+ if (type != &KeysType) {
PyErr_SetString(PyExc_TypeError, "we only support creating Keys");
- PyErr_BadInternalCall();
return NULL;
}
if (!PyTuple_CheckExact(args)) {
@@ -222,8 +384,8 @@
vt = v;
vt_to_decref = NULL;
} else {
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
}
if (Keys_CheckExact(w)) {
wt = Keys_as_tuple((Keys *)w);
@@ -233,8 +395,8 @@
wt_to_decref = NULL;
} else {
Py_XDECREF(vt_to_decref);
- Py_INCREF(Py_NotImplemented);
- return Py_NotImplemented;
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
}
/* Now we have 2 tuples to compare, do it */
result = PyTuple_Type.tp_richcompare(vt, wt, op);
@@ -303,22 +465,22 @@
}
-static char Keys_get_key_doc[] = "get_keys(offset)";
+static char Keys_as_tuple_doc[] = "as_tuple() => tuple";
static PyMethodDef Keys_methods[] = {
- {"as_tuple", (PyCFunction)Keys_as_tuple, METH_VARARGS},
+ {"as_tuple", (PyCFunction)Keys_as_tuple, METH_NOARGS, Keys_as_tuple_doc},
{NULL, NULL} /* sentinel */
};
static PySequenceMethods Keys_as_sequence = {
- (lenfunc)Keys_length, /* sq_length */
- 0, /* sq_concat */
- 0, /* sq_repeat */
- (ssizeargfunc)Keys_item, /* sq_item */
- 0, /* sq_slice */
- 0, /* sq_ass_item */
- 0, /* sq_ass_slice */
- 0, /* sq_contains */
+ (lenfunc)Keys_length, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ (ssizeargfunc)Keys_item, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ 0, /* sq_contains */
};
static PyTypeObject KeysType = {
@@ -381,6 +543,8 @@
{
PyObject* m;
+ if (PyType_Ready(&KeyType) < 0)
+ return;
if (PyType_Ready(&KeysType) < 0)
return;
@@ -389,6 +553,8 @@
if (m == NULL)
return;
+ Py_INCREF(&KeyType);
+ PyModule_AddObject(m, "Key", (PyObject *)&KeyType);
Py_INCREF(&KeysType);
PyModule_AddObject(m, "Keys", (PyObject *)&KeysType);
}
=== modified file 'bzrlib/_keys_type_py.py'
--- a/bzrlib/_keys_type_py.py 2009-09-10 17:28:10 +0000
+++ b/bzrlib/_keys_type_py.py 2009-09-10 18:15:17 +0000
@@ -21,6 +21,17 @@
"""
+def Key(*args):
+ """Implemented as just returning a tuple of the arguments supplied."""
+ for bit in args:
+ if not isinstance(bit, str):
+ raise TypeError('key bits must be strings')
+ num_keys = len(args)
+ if num_keys <= 0 or num_keys > 256:
+ raise ValueError('must have 1 => 256 key bits')
+ return args
+
+
def Keys(width, *args):
if not isinstance(width, int):
raise TypeError('width must be an integer.')
=== modified file 'bzrlib/tests/test__keys_type.py'
--- a/bzrlib/tests/test__keys_type.py 2009-09-10 17:28:10 +0000
+++ b/bzrlib/tests/test__keys_type.py 2009-09-10 18:15:17 +0000
@@ -59,6 +59,35 @@
CompiledKeysType = _CompiledKeysType()
+class TestKeyType(tests.TestCase):
+
+ def test_create(self):
+ k = self.module.Key('foo')
+ k = self.module.Key('foo', 'bar')
+
+ def test_create_bad_args(self):
+ self.assertRaises(ValueError, self.module.Key)
+ lots_of_args = ['a']*300
+ # too many args
+ self.assertRaises(ValueError, self.module.Key, *lots_of_args)
+ # not a string
+ self.assertRaises(TypeError, self.module.Key, 10)
+
+ def test_as_tuple(self):
+ k = self.module.Key('foo')
+ if getattr(k, 'as_tuple', None) is None:
+ t = k
+ else:
+ t = k.as_tuple()
+ self.assertEqual(('foo',), t)
+ k = self.module.Key('foo', 'bar')
+ if getattr(k, 'as_tuple', None) is None:
+ t = k
+ else:
+ t = k.as_tuple()
+ self.assertEqual(('foo', 'bar'), t)
+
+
class TestKeysType(tests.TestCase):
def test_create(self):
More information about the bazaar-commits
mailing list