55fa09d084977a66938606370fe2a19aed42d5fc
[jelmer/dulwich-libgit2.git] / dulwich / _pack.c
1 /* 
2  * Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; version 2
7  * of the License or (at your option) a later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  * MA  02110-1301, USA.
18  */
19
20 #include <Python.h>
21 #include <stdint.h>
22
23 static int py_is_sha(PyObject *sha)
24 {
25     if (!PyString_CheckExact(sha))
26         return 0;
27
28     if (PyString_Size(sha) != 20)
29         return 0;
30
31     return 1;
32 }
33
34
35 static size_t get_delta_header_size(uint8_t *delta, int *index, int length)
36 {
37         size_t size = 0;
38         int i = 0;
39         while ((*index) < length) {
40                 uint8_t cmd = delta[*index];
41                 (*index)++;
42                 size |= (cmd & ~0x80) << i;
43                 i += 7;
44                 if (!(cmd & 0x80))
45                         break;
46         }
47         return size;
48 }
49
50
51 static PyObject *py_apply_delta(PyObject *self, PyObject *args)
52 {
53         uint8_t *src_buf, *delta;
54         int src_buf_len, delta_len;
55         size_t src_size, dest_size;
56         size_t outindex = 0;
57         int index;
58         uint8_t *out;
59         PyObject *ret;
60
61         if (!PyArg_ParseTuple(args, "s#s#", (uint8_t *)&src_buf, &src_buf_len, 
62                                                   (uint8_t *)&delta, &delta_len))
63                 return NULL;
64
65     index = 0;
66     src_size = get_delta_header_size(delta, &index, delta_len);
67     if (src_size != src_buf_len) {
68                 PyErr_Format(PyExc_ValueError, 
69                         "Unexpected source buffer size: %lu vs %d", src_size, src_buf_len);
70                 return NULL;
71         }
72     dest_size = get_delta_header_size(delta, &index, delta_len);
73         ret = PyString_FromStringAndSize(NULL, dest_size);
74         if (ret == NULL) {
75                 PyErr_NoMemory();
76                 return NULL;
77         }
78         out = (uint8_t *)PyString_AsString(ret);
79     while (index < delta_len) {
80         char cmd = delta[index];
81         index++;
82         if (cmd & 0x80) {
83             size_t cp_off = 0, cp_size = 0;
84                         int i;
85             for (i = 0; i < 4; i++) {
86                 if (cmd & (1 << i)) {
87                     uint8_t x = delta[index];
88                     index++;
89                     cp_off |= x << (i * 8);
90                                 }
91                         }
92             for (i = 0; i < 3; i++) {
93                 if (cmd & (1 << (4+i))) {
94                     uint8_t x = delta[index];
95                     index++;
96                     cp_size |= x << (i * 8);
97                                 }
98                         }
99             if (cp_size == 0)
100                 cp_size = 0x10000;
101             if (cp_off + cp_size < cp_size ||
102                 cp_off + cp_size > src_size ||
103                 cp_size > dest_size)
104                 break;
105                         memcpy(out+outindex, src_buf+cp_off, cp_size);
106                         outindex += cp_size;
107                 } else if (cmd != 0) {
108                         memcpy(out+outindex, delta+index, cmd);
109                         outindex += cmd;
110             index += cmd;
111                 } else {
112                         PyErr_SetString(PyExc_ValueError, "Invalid opcode 0");
113                         return NULL;
114                 }
115         }
116     
117     if (index != delta_len) {
118                 PyErr_SetString(PyExc_ValueError, "delta not empty");
119                 return NULL;
120         }
121
122         if (dest_size != outindex) {
123         PyErr_SetString(PyExc_ValueError, "dest size incorrect");
124                 return NULL;
125         }
126
127     return ret;
128 }
129
130 static PyObject *py_bisect_find_sha(PyObject *self, PyObject *args)
131 {
132     PyObject *unpack_name;
133     char *sha;
134     int sha_len;
135         int start, end;
136     if (!PyArg_ParseTuple(args, "iis#O", &start, &end, 
137                                                   &sha, &sha_len, &unpack_name))
138         return NULL;
139
140     if (sha_len != 20) {
141         PyErr_SetString(PyExc_ValueError, "Sha is not 20 bytes long");
142         return NULL;
143     }
144     if (start > end) {
145         PyErr_SetString(PyExc_AssertionError, "start > end");
146         return NULL;
147     }
148
149     while (start <= end) {
150         PyObject *file_sha;
151         int i = (start + end)/2;
152         int cmp;
153         file_sha = PyObject_CallFunction(unpack_name, "i", i);
154         if (file_sha == NULL) {
155             return NULL;
156         }
157         if (!py_is_sha(file_sha)) {
158             PyErr_SetString(PyExc_TypeError, "unpack_name returned non-sha object");
159                         Py_DECREF(file_sha);
160             return NULL;
161         }
162         cmp = memcmp(PyString_AsString(file_sha), sha, 20);
163                 Py_DECREF(file_sha);
164         if (cmp < 0)
165             start = i + 1;
166         else if (cmp > 0)
167             end = i - 1;
168         else {
169                         return PyInt_FromLong(i);
170         }
171     }
172     Py_RETURN_NONE;
173 }
174
175
176 static PyMethodDef py_pack_methods[] = {
177         { "apply_delta", (PyCFunction)py_apply_delta, METH_VARARGS, NULL },
178     { "bisect_find_sha", (PyCFunction)py_bisect_find_sha, METH_VARARGS, NULL },
179 };
180
181 void init_pack(void)
182 {
183         PyObject *m;
184
185         m = Py_InitModule3("_pack", py_pack_methods, NULL);
186         if (m == NULL)
187                 return;
188 }