Skip to content

Commit b38b95c

Browse files
d-w-mooretrel
authored andcommitted
[#104] add a setbuf method for BytesBuf structure.
Also allow freeing bytesBuf heap pointer when the Python object goes out of scope. Added borrowed ref in set_buffer; bounds checking in get_byte; and overloaded function giving (in effect) a default of -1 for length. (I.e. auto-sense the length of the passed object.)
1 parent 5bf88f4 commit b38b95c

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,40 @@ def get_irods_username (rule_args, callback, rei):
190190
rule_args [0] = username
191191
```
192192

193+
## Special methods
194+
195+
Some iRODS objects used with rules and microservices have been given utility methods.
196+
197+
An example is the map() method of a `PluginContext` PEP argument:
198+
```
199+
def pep_resource_resolve_hierarchy_pre(rule_args, callback, rei):
200+
import pprint
201+
pprint.pprint(rule_args[1].map().items())
202+
```
203+
204+
In version 4.2.11.2 of the Python plugin, `BytesBuf` has methods to set and access byte content.
205+
These can be used to achieve true binary reads and writes, to and from data objects.
206+
207+
For example:
208+
```
209+
def my_rule(args, callback, rei):
210+
buf = irods_types.BytesBuf()
211+
buf.set_buffer( os.urandom(256) )
212+
callback.msiDataObjectWrite( descriptor, buf, 0) #-- Write the buffer.
213+
# ...
214+
buf.clear_buffer()
215+
```
216+
or:
217+
```
218+
# ...
219+
retv = callback.msiDataObjectRead( descriptor, "256", irods_types.BytesBuf())
220+
returnbuf = retv['arguments'][2]
221+
out = returnbuf.get_bytes() #-- Returns list of integer octets.
222+
start_byte = returnbuf.get_byte(0) #-- Returns first octet
223+
```
224+
225+
226+
193227
## `genquery.py`
194228

195229
This module offers writers of Python rules a convenient facility for querying and iterating over objects in the iRODS object catalog (ICAT).

irods_types.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ bool operator==(const sqlResult_t& s1, const sqlResult_t& s2) {
3131
}
3232

3333
namespace {
34+
35+
void set_buffer_2argument_form_(bytesBuf_t* self, PyObject *c, int L)
36+
{
37+
bp::handle<> handle{bp::borrowed(c)}; // The PyObject*c will be "borrowed" in the context of this function, meaning
38+
bp::object obj {handle}; // we increment its reference count until the handle goes out of scope.
39+
if (L < 0) {
40+
L = bp::len(obj);
41+
}
42+
self->buf = realloc(self->buf, L);
43+
if (self->buf) {
44+
self->len = L;
45+
auto *buf_dest = reinterpret_cast<unsigned char *>(self->buf);
46+
for (int i=0; i<L; i++) {
47+
*buf_dest++ = bp::extract<unsigned char>{ obj[i] };
48+
}
49+
}
50+
}
51+
52+
void set_buffer( bytesBuf_t* self, PyObject *c) { set_buffer_2argument_form_(self,c,-1); }
53+
3454
BOOST_PYTHON_MODULE(irods_errors)
3555
{
3656
std::map <std::string, int> irods_constants;
@@ -1007,6 +1027,27 @@ namespace {
10071027
.def("__init__", make_init_function<bytesBuf_t>(
10081028
&bytesBuf_t::len,
10091029
&bytesBuf_t::buf))
1030+
.def("get_byte", +[]( bytesBuf_t* s, std::size_t n)->unsigned char {
1031+
if (n >= s->len) {
1032+
PyErr_SetString(PyExc_IndexError, "BytesBuf out-of-range access");
1033+
throw bp::error_already_set();
1034+
}
1035+
return static_cast<char*>(s->buf)[n];
1036+
})
1037+
.def("get_bytes", +[]( bytesBuf_t* s) {
1038+
bp::object None;
1039+
bp::list a{};
1040+
a.append(None); // construct a list of length 1 and expand it...
1041+
a *= s->len; // ... to the desired length.
1042+
auto buf_src = static_cast<unsigned char*>(s->buf);
1043+
for (int j=0; j<s->len; j++) {
1044+
a[j]=*buf_src++;
1045+
}
1046+
return a;
1047+
})
1048+
.def("clear_buffer", clearBBuf)
1049+
.def("set_buffer",set_buffer)
1050+
.def("set_buffer",set_buffer_2argument_form_)
10101051
.add_property("len", &bytesBuf_t::len)
10111052
.add_property("buf", +[](bytesBuf_t *s) { return s->buf ? bp::object{array_ref<char>{static_cast<char*>(s->buf), static_cast<std::size_t>(s->len)}} : bp::object{}; })
10121053
;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
def main(rule_args, callback, rei):
2+
return data_obj_binary_write_test__104(callback)
3+
4+
class error_tracker:
5+
6+
def __init__(self):
7+
self.code = 0
8+
9+
def assert_(self,
10+
nonzero_value,
11+
bool_lambda = lambda:False): # The lambda is for lazy evaluation.
12+
# `nonzero_value' should be negative and not be
13+
# the same in absolute value as any valid errno code.
14+
if self.code == 0:
15+
self.code = (0 if bool_lambda() else nonzero_value)
16+
17+
def data_obj_binary_write_test__104(callback):
18+
19+
binary_bytes = irods_types.BytesBuf()
20+
21+
buffer = bytearray((256 + 127 - i) % 256 for i in range(256)) # Create a list of [127,126,...,0,255,254,...,128]
22+
buffer_length = len(buffer)
23+
dest_file = '/tempZone/home/rods/test_buffer__104'
24+
25+
binary_bytes.set_buffer(buffer)
26+
27+
err = error_tracker()
28+
try:
29+
binary_bytes.get_byte(10240)
30+
except IndexError:
31+
pass # This error is the expected result of get_byte with too large an index.
32+
else:
33+
err.assert_(-997) # Error because an exception should have been thrown by the out-of-range access.
34+
35+
ret_val = callback.msiDataObjOpen(
36+
('objPath={}++++openFlags=O_RDWR''O_CREAT''O_TRUNC').format(dest_file)
37+
, 0)
38+
f = ret_val['arguments'][1]
39+
40+
callback.msiDataObjWrite(f, binary_bytes, buffer_length)
41+
binary_bytes.clear_buffer()
42+
43+
callback.msiDataObjLseek(f, "0", "SEEK_SET", 0)
44+
45+
ret_val = callback.msiDataObjRead(f, str(16+buffer_length), irods_types.BytesBuf())
46+
read_buffer = ret_val['arguments'][2]
47+
48+
err.assert_(-999, lambda: list(buffer) == read_buffer.get_bytes())
49+
err.assert_(-998, lambda: buffer_length == read_buffer.len)
50+
51+
read_buffer.clear_buffer()
52+
53+
callback.msiDataObjClose(f, 0)
54+
callback.msiDataObjUnlink("objPath="+dest_file,0)
55+
56+
return err.code
57+
58+
INPUT null
59+
OUTPUT ruleExecOut

0 commit comments

Comments
 (0)