-
Notifications
You must be signed in to change notification settings - Fork 226
Expand file tree
/
Copy pathStrided1DNIOBuffer.java
More file actions
208 lines (186 loc) · 10.4 KB
/
Strided1DNIOBuffer.java
File metadata and controls
208 lines (186 loc) · 10.4 KB
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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package org.python.core.buffer;
import java.nio.ByteBuffer;
import org.python.core.BufferProtocol;
import org.python.core.PyBuffer;
import org.python.core.PyException;
/**
* Read-only buffer API over a one-dimensional <code>java.nio.ByteBuffer</code> of one-byte items,
* that are evenly-spaced in that store. The buffer has <code>storage</code>, <code>index0</code>
* and <code>length</code> properties in the usual way, designating a slice (or all) of a byte
* array, but also a <code>stride</code> property (equal to <code>getStrides()[0]</code>).
* <p>
* Let the underlying buffer be the byte sequence <i>u(i)</i> for <i>i=0..N-1</i>, let <i>x</i> be
* the <code>Strided1DNIOBuffer</code>, and let the stride be <i>p</i>. The storage works as
* follows. Designate by <i>x(j)</i>, for <i>j=0..L-1</i>, the byte at index <i>j</i>, that is, the
* byte retrieved by <code>x.byteAt(j)</code>. Thus, we store <i>x(j)</i> at <i>u(a+pj)</i>, that
* is, <i>x(0) = u(a)</i>. When we construct such a buffer, we have to supply <i>a</i> =
* <code>index0</code>, <i>L</i> = <code>count</code>, and <i>p</i> = <code>stride</code> as the
* constructor arguments. The last item in the slice <i>x(L-1)</i> is stored at <i>u(a+p(L-1))</i>.
* For the simple case of positive stride, constructor argument <code>index0</code> is the low index
* of the range occupied by the data. When the stride is negative, that is to say <i>p<0</i>, and
* <i>L>1</i>, this will be to the left of <i>u(a)</i>, and the constructor argument
* <code>index0</code> is not then the low index of the range occupied by the data. Clearly both
* these indexes must be in the range 0 to <i>N-1</i> inclusive, a rule enforced by the constructors
* (unless <i>L=0</i>, when it is assumed no array access will take place).
* <p>
* The class may be used by exporters to create a strided slice (e.g. to export the diagonal of a
* matrix) and in particular by other buffers to create strided slices of themselves, such as to
* create the <code>memoryview</code> that is returned as an extended slice of a
* <code>memoryview</code>.
*/
public class Strided1DNIOBuffer extends BaseNIOBuffer {
/**
* Step size in the underlying buffer essential to correct translation of an index (or indices)
* into an index into the storage. The value is returned by {@link #getStrides()} is an array
* with this as the only element.
*/
protected int stride;
/**
* Provide an instance of <code>Strided1DNIOBuffer</code> with navigation variables initialised,
* for sub-class use. The buffer ({@link #storage}, {@link #index0}), and the navigation (
* {@link #shape} array and {@link #stride}) will be initialised from the arguments (which are
* checked for range).
* <p>
* The sub-class constructor should check that the intended access is compatible with this
* object by calling {@link #checkRequestFlags(int)}. (See the source of
* {@link Strided1DWritableBuffer} for an example of this use.)
*
* @param obj exporting object (or <code>null</code>)
* @param storage the <code>ByteBuffer</code> wrapping the exported object state. NOTE: this
* <code>PyBuffer</code> keeps a reference and may manipulate the position, mark and
* limit hereafter. Use {@link ByteBuffer#duplicate()} to give it an isolated copy.
* @param index0 index into storage of item[0]
* @param count number of items in the slice
* @param stride in between successive elements of the new PyBuffer
* @throws NullPointerException if <code>storage</code> is null
* @throws ArrayIndexOutOfBoundsException if <code>index0</code>, <code>count</code> and
* <code>stride</code> are inconsistent with <code>storage.length</code>
*/
protected Strided1DNIOBuffer(BufferProtocol obj, ByteBuffer storage, int index0, int count,
int stride) throws ArrayIndexOutOfBoundsException, NullPointerException {
super(storage, STRIDES, index0, count, stride);
this.obj = obj;
this.stride = stride; // Between items
if (count == 0) {
// Nothing to check as we'll make no accesses
addFeatureFlags(CONTIGUITY);
} else {
// Need to check lowest and highest index against array
int lo, hi;
if (stride == 1) {
lo = index0; // First byte of item[0]
hi = index0 + count; // Last byte of item[L-1] + 1
addFeatureFlags(CONTIGUITY);
} else if (stride > 1) {
lo = index0; // First byte of item[0]
hi = index0 + (count - 1) * stride + 1; // Last byte of item[L-1] + 1
} else {
hi = index0 + 1; // Last byte of item[0] + 1
lo = index0 + (count - 1) * stride; // First byte of item[L-1]
}
// Check indices using "all non-negative" trick
int cap = storage.capacity();
if ((count | lo | (cap - lo) | hi | (cap - hi)) < 0) {
throw new ArrayIndexOutOfBoundsException();
}
}
// Deduce feature flags from the client's ByteBuffer
if (!storage.isReadOnly()) {
addFeatureFlags(WRITABLE);
}
if (storage.hasArray()) {
addFeatureFlags(AS_ARRAY);
}
}
/**
* Provide an instance of <code>Strided1DNIOBuffer</code> on a particular {@link ByteBuffer}
* specifying a starting index, the number of items in the result, and a byte-indexing stride.
* The result of <code>byteAt(i)</code> will be equal to
* <code>storage.get(index0+stride*i)</code> (whatever the sign of <code>stride</code>), valid
* for <code>0<=i<count</code>. The constructor checks that all these indices lie within
* the <code>storage</code> (unless <code>count=0</code>). No reference will be kept to the
* <code>ByteBuffer</code> passed in. (It is duplicated.)
* <p>
* The constructed <code>PyBuffer</code> meets the consumer's expectations as expressed in the
* <code>flags</code> argument, or an exception will be thrown if these are incompatible with
* the type (e.g. the consumer does not specify that it understands the strides array). Note
* that the actual range in the <code>storage</code> array, the lowest and highest index, is not
* explicitly passed, but is implicit in <code>index0</code>, <code>count</code> and
* <code>stride</code>. The constructor checks that these indices lie within the
* <code>storage</code> array (unless <code>count=0</code>).
*
* @param flags consumer requirements
* @param obj exporting object (or <code>null</code>)
* @param storage <code>ByteBuffer</code> wrapping exported data
* @param index0 index into storage of item[0]
* @param count number of items in the slice
* @param stride in between successive elements of the new PyBuffer
* @throws NullPointerException if <code>storage</code> is null
* @throws ArrayIndexOutOfBoundsException if <code>index0</code>, <code>count</code> and
* <code>stride</code> are inconsistent with <code>storage.length</code>
* @throws PyException {@code BufferError} when expectations do not correspond with the type
*/
public Strided1DNIOBuffer(int flags, BufferProtocol obj, ByteBuffer storage, int index0,
int count, int stride) throws ArrayIndexOutOfBoundsException, NullPointerException,
PyException {
this(obj, storage.duplicate(), index0, count, stride);
checkRequestFlags(flags); // Check request is compatible with type
}
@Override
public final int byteIndex(int index) throws IndexOutOfBoundsException {
return index0 + index * stride;
}
/**
* {@inheritDoc}
* <p>
* <code>Strided1DNIOBuffer</code> provides an implementation for slicing already-strided bytes
* in one dimension. In that case, <i>x(i) = u(r+ip)</i> for <i>i = 0..L-1</i> where u is the
* underlying buffer, and <i>r</i>, <i>p</i> and <i>L</i> are the start, stride and count with
* which <i>x</i> was created from <i>u</i>. Thus <i>y(k) = u(r+sp+kmp)</i>, that is, the
* composite <code>index0</code> is <i>r+sp</i> and the composite <code>stride</code> is
* <i>mp</i>.
*/
@Override
public PyBuffer getBufferSlice(int flags, int start, int count, int stride) {
if (count > 0) {
// Translate start relative to underlying buffer
int compStride = this.stride * stride;
int compIndex0 = index0 + start * this.stride;
// Construct a view, taking a lock on the root object (this or this.root)
return new SlicedView(getRoot(), flags, storage, compIndex0, count, compStride);
} else {
// Special case for count==0 where above logic would fail. Efficient too.
return new ZeroByteBuffer.View(getRoot(), flags);
}
}
/**
* A <code>Strided1DNIOBuffer.SlicedView</code> represents a non-contiguous subsequence of a
* simple buffer.
*/
static class SlicedView extends Strided1DNIOBuffer {
/** The buffer on which this is a slice view */
PyBuffer root;
/**
* Construct a slice of a one-dimensional byte buffer.
*
* @param root on which release must be called when this is released
* @param flags consumer requirements
* @param storage <code>ByteBuffer</code> wrapping exported data (no reference kept)
* @param index0 index into storage of item[0]
* @param count the number of items in the sliced view
* @param stride in between successive elements of the new PyBuffer
* @throws PyException {@code BufferError} when expectations do not correspond with the type
*/
public SlicedView(PyBuffer root, int flags, ByteBuffer storage, int index0, int count,
int stride) throws PyException {
// Create a new slice on the buffer passed in (part of the root)
super(flags, root.getObj(), storage, index0, count, stride);
// Get a lease on the root PyBuffer (read-only)
this.root = root.getBuffer(FULL_RO);
}
@Override
protected PyBuffer getRoot() {
return root;
}
}
}