~bzr-pqm/bzr/bzr.dev

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/* Copyright (C) 2009 Canonical Ltd
 * 
 * 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; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _STATIC_TUPLE_H_
#define _STATIC_TUPLE_H_
#include <Python.h>
#include <string.h>

#define STATIC_TUPLE_HAS_HASH 0
/* Caching the hash adds memory, but allows us to save a little time during
 * lookups. TIMEIT hash(key) shows it as
 *  0.108usec w/ hash
 *  0.160usec w/o hash
 * Note that the entries themselves are strings, which already cache their
 * hashes. So while there is a 1.5:1 difference in the time for hash(), it is
 * already a function which is quite fast. Probably the only reason we might
 * want to do so, is if we implement a KeyIntern dict that assumes it is
 * available, and can then drop the 'hash' value from the item pointers. Then
 * again, if Key_hash() is fast enough, we may not even care about that.
 */

/* 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.
 */

#define STATIC_TUPLE_INTERNED_FLAG 0x01
#define STATIC_TUPLE_ALL_STRING    0x02
#define STATIC_TUPLE_DID_HASH      0x04
typedef struct {
    PyObject_HEAD
    unsigned char size;
    unsigned char flags;
    unsigned char _unused0;
    unsigned char _unused1;
    // Note that on 64-bit, we actually have 4-more unused bytes
    // because items will always be aligned to a 64-bit boundary
#if STATIC_TUPLE_HAS_HASH
    long hash;
#endif
    PyObject *items[0];
} StaticTuple;
extern PyTypeObject StaticTuple_Type;

typedef struct {
    PyObject_VAR_HEAD
    PyObject *table[0];
} KeyIntern;

#define StaticTuple_CheckExact(op) (Py_TYPE(op) == &StaticTuple_Type)
#define StaticTuple_SET_ITEM(key, offset, val) \
    ((((StaticTuple*)(key))->items[(offset)]) = ((PyObject *)(val)))
#define StaticTuple_GET_ITEM(key, offset) (((StaticTuple*)key)->items[offset])


static const char *_C_API_NAME = "_C_API";

#ifdef STATIC_TUPLE_MODULE
/* Used when compiling _static_tuple_c.c */

static StaticTuple * StaticTuple_New(Py_ssize_t);
static StaticTuple * StaticTuple_intern(StaticTuple *self);

#else
/* Used as the foreign api */

static StaticTuple *(*StaticTuple_New)(Py_ssize_t);
static StaticTuple *(*StaticTuple_intern)(StaticTuple *);
static PyTypeObject *_p_StaticTuple_Type;

#define StaticTuple_CheckExact(op) (Py_TYPE(op) == _p_StaticTuple_Type)
static int (*_StaticTuple_CheckExact)(PyObject *);


static int _import_function(PyObject *module, char *funcname,
                            void **f, char *signature)
{
    PyObject *d = NULL;
    PyObject *c_obj = NULL;
    char *desc = NULL;

    d = PyObject_GetAttrString(module, _C_API_NAME);
    if (!d) {
        // PyObject_GetAttrString sets an appropriate exception
        goto bad;
    }
    c_obj = PyDict_GetItemString(d, funcname);
    if (!c_obj) {
        // PyDict_GetItemString does not set an exception
        PyErr_Format(PyExc_AttributeError,
            "Module %s did not export a function named %s\n",
            PyModule_GetName(module), funcname);
        goto bad;
    }
    desc = (char *)PyCObject_GetDesc(c_obj);
    if (!desc || strcmp(desc, signature) != 0) {
        if (desc == NULL) {
            desc = "<null>";
        }
        PyErr_Format(PyExc_TypeError,
            "C function %s.%s has wrong signature (expected %s, got %s)",
                PyModule_GetName(module), funcname, signature, desc);
        goto bad;
    }
    *f = PyCObject_AsVoidPtr(c_obj);
    fprintf(stderr, "Imported function %s @%x\n", funcname, *f);
    Py_DECREF(d);
    return 0;
bad:
    fprintf(stderr, "Returning -1\n");
    Py_XDECREF(d);
    return -1;
}


static PyTypeObject *
_import_type(PyObject *module, char *class_name)
{
    PyObject *type = NULL;

    type = PyObject_GetAttrString(module, class_name);
    if (!type) {
        goto bad;
    }
    if (!PyType_Check(type)) {
        PyErr_Format(PyExc_TypeError,
            "%s.%s is not a type object",
            PyModule_GetName(module), class_name);
        goto bad;
    }
    return (PyTypeObject *)type;
bad:
    Py_XDECREF(type);
    return NULL;
}


/* Return -1 and set exception on error, 0 on success */
static int
import_static_tuple_c(void)
{
    /* This is modeled after the implementation in Pyrex, which uses a
     * dictionary and descriptors, rather than using plain offsets into a
     * void ** array.
     */
    PyObject *module = NULL;
    
    module = PyImport_ImportModule("bzrlib._static_tuple_c");
    if (!module) goto bad;
    if (_import_function(module, "StaticTuple_New", (void **)&StaticTuple_New,
                         "StaticTuple *(Py_ssize_t)") < 0)
        goto bad;
    if (_import_function(module, "StaticTuple_intern",
                         (void **)&StaticTuple_intern,
                         "StaticTuple *(StaticTuple *)") < 0)
        goto bad;
    if (_import_function(module, "_StaticTuple_CheckExact",
                         (void **)&_StaticTuple_CheckExact,
                         "int(PyObject *)") < 0)
        goto bad;
    _p_StaticTuple_Type = _import_type(module, "StaticTuple");
    if (!_p_StaticTuple_Type) {
        goto bad;
    }
    Py_DECREF(module); 
    return 0;
bad:
    Py_XDECREF(module);
    return -1;
}

#endif // !STATIC_TUPLE_MODULE
#endif // !_STATIC_TUPLE_H_