##// END OF EJS Templates
rust-dirstate: architecture independence fix...
Georges Racinet -
r42600:4e4fa3a9 default
parent child Browse files
Show More
@@ -1,227 +1,227 b''
1 // dirstate.rs
1 // dirstate.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dirstate` module provided by the
8 //! Bindings for the `hg::dirstate` module provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10 //!
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
12
12
13 use cpython::{
13 use cpython::{
14 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, PyResult,
14 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, PyResult,
15 PySequence, PythonObject, PyTuple, Python, ToPyObject,
15 PySequence, PythonObject, PyTuple, Python, ToPyObject,
16 };
16 };
17 use hg::{
17 use hg::{
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
19 DirstatePackError, DirstateParents, DirstateParseError, DirstateVec,
19 DirstatePackError, DirstateParents, DirstateParseError, DirstateVec,
20 };
20 };
21 use std::collections::HashMap;
21 use std::collections::HashMap;
22 use std::ffi::CStr;
22 use std::ffi::CStr;
23 #[cfg(feature = "python27")]
23 #[cfg(feature = "python27")]
24 extern crate python27_sys as python_sys;
24 extern crate python27_sys as python_sys;
25 #[cfg(feature = "python3")]
25 #[cfg(feature = "python3")]
26 extern crate python3_sys as python_sys;
26 extern crate python3_sys as python_sys;
27 use self::python_sys::PyCapsule_Import;
27 use self::python_sys::PyCapsule_Import;
28 use libc::{c_char, c_int};
28 use libc::{c_char, c_int};
29 use std::mem::transmute;
29 use std::mem::transmute;
30
30
31 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
31 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
32 /// for this type, and raises a Python `Exception` if the check does not pass.
32 /// for this type, and raises a Python `Exception` if the check does not pass.
33 /// Because this type differs only in name from the regular Python tuple, it
33 /// Because this type differs only in name from the regular Python tuple, it
34 /// would be a good idea in the near future to remove it entirely to allow
34 /// would be a good idea in the near future to remove it entirely to allow
35 /// for a pure Python tuple of the same effective structure to be used,
35 /// for a pure Python tuple of the same effective structure to be used,
36 /// rendering this type and the capsule below useless.
36 /// rendering this type and the capsule below useless.
37 type MakeDirstateTupleFn = extern "C" fn(
37 type MakeDirstateTupleFn = extern "C" fn(
38 state: c_char,
38 state: c_char,
39 mode: c_int,
39 mode: c_int,
40 size: c_int,
40 size: c_int,
41 mtime: c_int,
41 mtime: c_int,
42 ) -> PyObject;
42 ) -> PyObject;
43
43
44 /// This is largely a copy/paste from cindex.rs, pending the merge of a
44 /// This is largely a copy/paste from cindex.rs, pending the merge of a
45 /// `py_capsule_fn!` macro in the rust-cpython project:
45 /// `py_capsule_fn!` macro in the rust-cpython project:
46 /// https://github.com/dgrunwald/rust-cpython/pull/169
46 /// https://github.com/dgrunwald/rust-cpython/pull/169
47 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
47 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
48 unsafe {
48 unsafe {
49 let caps_name = CStr::from_bytes_with_nul_unchecked(
49 let caps_name = CStr::from_bytes_with_nul_unchecked(
50 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
50 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
51 );
51 );
52 let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0);
52 let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0);
53 if from_caps.is_null() {
53 if from_caps.is_null() {
54 return Err(PyErr::fetch(py));
54 return Err(PyErr::fetch(py));
55 }
55 }
56 Ok(transmute(from_caps))
56 Ok(transmute(from_caps))
57 }
57 }
58 }
58 }
59
59
60 fn parse_dirstate_wrapper(
60 fn parse_dirstate_wrapper(
61 py: Python,
61 py: Python,
62 dmap: PyDict,
62 dmap: PyDict,
63 copymap: PyDict,
63 copymap: PyDict,
64 st: PyBytes,
64 st: PyBytes,
65 ) -> PyResult<PyTuple> {
65 ) -> PyResult<PyTuple> {
66 match parse_dirstate(st.data(py)) {
66 match parse_dirstate(st.data(py)) {
67 Ok((parents, dirstate_vec, copies)) => {
67 Ok((parents, dirstate_vec, copies)) => {
68 for (filename, entry) in dirstate_vec {
68 for (filename, entry) in dirstate_vec {
69 dmap.set_item(
69 dmap.set_item(
70 py,
70 py,
71 PyBytes::new(py, &filename[..]),
71 PyBytes::new(py, &filename[..]),
72 decapsule_make_dirstate_tuple(py)?(
72 decapsule_make_dirstate_tuple(py)?(
73 entry.state,
73 entry.state as c_char,
74 entry.mode,
74 entry.mode,
75 entry.size,
75 entry.size,
76 entry.mtime,
76 entry.mtime,
77 ),
77 ),
78 )?;
78 )?;
79 }
79 }
80 for CopyVecEntry { path, copy_path } in copies {
80 for CopyVecEntry { path, copy_path } in copies {
81 copymap.set_item(
81 copymap.set_item(
82 py,
82 py,
83 PyBytes::new(py, path),
83 PyBytes::new(py, path),
84 PyBytes::new(py, copy_path),
84 PyBytes::new(py, copy_path),
85 )?;
85 )?;
86 }
86 }
87 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
87 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
88 .to_py_object(py))
88 .to_py_object(py))
89 }
89 }
90 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
90 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
91 py,
91 py,
92 match e {
92 match e {
93 DirstateParseError::TooLittleData => {
93 DirstateParseError::TooLittleData => {
94 "too little data for parents".to_string()
94 "too little data for parents".to_string()
95 }
95 }
96 DirstateParseError::Overflow => {
96 DirstateParseError::Overflow => {
97 "overflow in dirstate".to_string()
97 "overflow in dirstate".to_string()
98 }
98 }
99 DirstateParseError::CorruptedEntry(e) => e,
99 DirstateParseError::CorruptedEntry(e) => e,
100 },
100 },
101 )),
101 )),
102 }
102 }
103 }
103 }
104
104
105 fn pack_dirstate_wrapper(
105 fn pack_dirstate_wrapper(
106 py: Python,
106 py: Python,
107 dmap: PyDict,
107 dmap: PyDict,
108 copymap: PyDict,
108 copymap: PyDict,
109 pl: PyTuple,
109 pl: PyTuple,
110 now: PyInt,
110 now: PyInt,
111 ) -> PyResult<PyBytes> {
111 ) -> PyResult<PyBytes> {
112 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
112 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
113 let p1: &[u8] = p1.data(py);
113 let p1: &[u8] = p1.data(py);
114 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
114 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
115 let p2: &[u8] = p2.data(py);
115 let p2: &[u8] = p2.data(py);
116
116
117 let dirstate_vec: Result<DirstateVec, PyErr> = dmap
117 let dirstate_vec: Result<DirstateVec, PyErr> = dmap
118 .items(py)
118 .items(py)
119 .iter()
119 .iter()
120 .map(|(filename, stats)| {
120 .map(|(filename, stats)| {
121 let stats = stats.extract::<PySequence>(py)?;
121 let stats = stats.extract::<PySequence>(py)?;
122 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
122 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
123 let state = state.data(py)[0] as i8;
123 let state = state.data(py)[0] as i8;
124 let mode = stats.get_item(py, 1)?.extract(py)?;
124 let mode = stats.get_item(py, 1)?.extract(py)?;
125 let size = stats.get_item(py, 2)?.extract(py)?;
125 let size = stats.get_item(py, 2)?.extract(py)?;
126 let mtime = stats.get_item(py, 3)?.extract(py)?;
126 let mtime = stats.get_item(py, 3)?.extract(py)?;
127 let filename = filename.extract::<PyBytes>(py)?;
127 let filename = filename.extract::<PyBytes>(py)?;
128 let filename = filename.data(py);
128 let filename = filename.data(py);
129 Ok((
129 Ok((
130 filename.to_owned(),
130 filename.to_owned(),
131 DirstateEntry {
131 DirstateEntry {
132 state,
132 state,
133 mode,
133 mode,
134 size,
134 size,
135 mtime,
135 mtime,
136 },
136 },
137 ))
137 ))
138 })
138 })
139 .collect();
139 .collect();
140
140
141 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
141 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
142 .items(py)
142 .items(py)
143 .iter()
143 .iter()
144 .map(|(key, value)| {
144 .map(|(key, value)| {
145 Ok((
145 Ok((
146 key.extract::<PyBytes>(py)?.data(py).to_owned(),
146 key.extract::<PyBytes>(py)?.data(py).to_owned(),
147 value.extract::<PyBytes>(py)?.data(py).to_owned(),
147 value.extract::<PyBytes>(py)?.data(py).to_owned(),
148 ))
148 ))
149 })
149 })
150 .collect();
150 .collect();
151
151
152 match pack_dirstate(
152 match pack_dirstate(
153 &dirstate_vec?,
153 &dirstate_vec?,
154 &copies?,
154 &copies?,
155 DirstateParents { p1, p2 },
155 DirstateParents { p1, p2 },
156 now.as_object().extract::<i32>(py)?,
156 now.as_object().extract::<i32>(py)?,
157 ) {
157 ) {
158 Ok((packed, new_dirstate_vec)) => {
158 Ok((packed, new_dirstate_vec)) => {
159 for (
159 for (
160 filename,
160 filename,
161 DirstateEntry {
161 DirstateEntry {
162 state,
162 state,
163 mode,
163 mode,
164 size,
164 size,
165 mtime,
165 mtime,
166 },
166 },
167 ) in new_dirstate_vec
167 ) in new_dirstate_vec
168 {
168 {
169 dmap.set_item(
169 dmap.set_item(
170 py,
170 py,
171 PyBytes::new(py, &filename[..]),
171 PyBytes::new(py, &filename[..]),
172 decapsule_make_dirstate_tuple(py)?(
172 decapsule_make_dirstate_tuple(py)?(
173 state, mode, size, mtime,
173 state as c_char, mode, size, mtime,
174 ),
174 ),
175 )?;
175 )?;
176 }
176 }
177 Ok(PyBytes::new(py, &packed))
177 Ok(PyBytes::new(py, &packed))
178 }
178 }
179 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
179 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
180 py,
180 py,
181 match error {
181 match error {
182 DirstatePackError::CorruptedParent => {
182 DirstatePackError::CorruptedParent => {
183 "expected a 20-byte hash".to_string()
183 "expected a 20-byte hash".to_string()
184 }
184 }
185 DirstatePackError::CorruptedEntry(e) => e,
185 DirstatePackError::CorruptedEntry(e) => e,
186 DirstatePackError::BadSize(expected, actual) => {
186 DirstatePackError::BadSize(expected, actual) => {
187 format!("bad dirstate size: {} != {}", actual, expected)
187 format!("bad dirstate size: {} != {}", actual, expected)
188 }
188 }
189 },
189 },
190 )),
190 )),
191 }
191 }
192 }
192 }
193
193
194 /// Create the module, with `__package__` given from parent
194 /// Create the module, with `__package__` given from parent
195 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
195 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
196 let dotted_name = &format!("{}.dirstate", package);
196 let dotted_name = &format!("{}.dirstate", package);
197 let m = PyModule::new(py, dotted_name)?;
197 let m = PyModule::new(py, dotted_name)?;
198 m.add(py, "__package__", package)?;
198 m.add(py, "__package__", package)?;
199 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
199 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
200 m.add(
200 m.add(
201 py,
201 py,
202 "parse_dirstate",
202 "parse_dirstate",
203 py_fn!(
203 py_fn!(
204 py,
204 py,
205 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
205 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
206 ),
206 ),
207 )?;
207 )?;
208 m.add(
208 m.add(
209 py,
209 py,
210 "pack_dirstate",
210 "pack_dirstate",
211 py_fn!(
211 py_fn!(
212 py,
212 py,
213 pack_dirstate_wrapper(
213 pack_dirstate_wrapper(
214 dmap: PyDict,
214 dmap: PyDict,
215 copymap: PyDict,
215 copymap: PyDict,
216 pl: PyTuple,
216 pl: PyTuple,
217 now: PyInt
217 now: PyInt
218 )
218 )
219 ),
219 ),
220 )?;
220 )?;
221
221
222 let sys = PyModule::import(py, "sys")?;
222 let sys = PyModule::import(py, "sys")?;
223 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
223 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
224 sys_modules.set_item(py, dotted_name, &m)?;
224 sys_modules.set_item(py, dotted_name, &m)?;
225
225
226 Ok(m)
226 Ok(m)
227 }
227 }
General Comments 0
You need to be logged in to leave comments. Login now