org.python.core.PyByteArray Maven / Gradle / Ivy
Show all versions of jython-slim Show documentation
package org.python.core;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import org.python.core.buffer.BaseBuffer;
import org.python.core.buffer.SimpleWritableBuffer;
import org.python.expose.ExposedClassMethod;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedNew;
import org.python.expose.ExposedType;
import org.python.expose.MethodType;
/**
* Implementation of Python bytearray
with a Java API that includes equivalents to most
* of the Python API. These Python equivalents accept a {@link PyObject} as argument, where you
* might have expected a byte[]
or PyByteArray
, in order to accommodate
* the full range of types accepted by the Python equivalent: usually, any PyObject
* that implements {@link BufferProtocol}, providing a one-dimensional array of bytes, is an
* acceptable argument. In the documentation, the reader will often see the terms "byte array" or
* "object viewable as bytes" instead of bytearray
when this broader scope is intended.
* This may relate to parameters, or to the target object itself (in text that applies equally to
* base or sibling classes).
*/
@Untraversable
@ExposedType(name = "bytearray", base = PyObject.class, doc = BuiltinDocs.bytearray_doc)
public class PyByteArray extends BaseBytes implements BufferProtocol {
/** The {@link PyType} of bytearray
. */
public static final PyType TYPE = PyType.fromClass(PyByteArray.class);
/**
* Constructs a zero-length Python bytearray
of explicitly-specified sub-type
*
* @param type explicit Jython type
*/
public PyByteArray(PyType type) {
super(type);
}
/**
* Constructs a zero-length Python bytearray
.
*/
public PyByteArray() {
super(TYPE);
}
/**
* Constructs zero-filled Python bytearray
of specified size.
*
* @param size of bytearray
*/
public PyByteArray(int size) {
super(TYPE);
init(size);
}
/**
* Constructs a bytearray
by copying values from int[].
*
* @param value source of the bytes (and size)
*/
public PyByteArray(int[] value) {
super(TYPE, value);
}
/**
* Constructs a new array filled exactly by a copy of the contents of the source, which is a
* bytearray
(or bytes
).
*
* @param value source of the bytes (and size)
*/
public PyByteArray(BaseBytes value) {
super(TYPE);
init(value);
}
/**
* Constructs a new array filled exactly by a copy of the contents of the source, which is a
* byte-oriented {@link PyBuffer}.
*
* @param value source of the bytes (and size)
*/
PyByteArray(PyBuffer value) {
super(TYPE);
init(value);
}
/**
* Constructs a new array filled exactly by a copy of the contents of the source, which is an
* object supporting the Jython version of the PEP 3118 buffer API.
*
* @param value source of the bytes (and size)
*/
public PyByteArray(BufferProtocol value) {
super(TYPE);
init(value);
}
/**
* Constructs a new array filled from an iterable of PyObject. The iterable must yield objects
* convertible to Python bytes (non-negative integers less than 256 or strings of length 1).
*
* @param value source of the bytes (and size)
*/
public PyByteArray(Iterable extends PyObject> value) {
super(TYPE);
init(value);
}
/**
* Constructs a new array by encoding a PyString argument to bytes. If the PyString is actually
* a PyUnicode, the encoding must be explicitly specified.
*
* @param arg primary argument from which value is taken
* @param encoding name of optional encoding (must be a string type)
* @param errors name of optional errors policy (must be a string type)
*/
public PyByteArray(PyString arg, PyObject encoding, PyObject errors) {
super(TYPE);
init(arg, encoding, errors);
}
/**
* Constructs a new array by encoding a PyString argument to bytes. If the PyString is actually
* a PyUnicode, the encoding must be explicitly specified.
*
* @param arg primary argument from which value is taken
* @param encoding name of optional encoding (may be null
to select the default for
* this installation)
* @param errors name of optional errors policy
*/
public PyByteArray(PyString arg, String encoding, String errors) {
super(TYPE);
init(arg, encoding, errors);
}
/**
* Constructs a new array by encoding a PyString argument to bytes. If the PyString is actually
* a PyUnicode, an exception is thrown saying that the encoding must be explicitly specified.
*
* @param arg primary argument from which value is taken
*/
public PyByteArray(PyString arg) {
super(TYPE);
init(arg, (String)null, (String)null);
}
/**
* Constructs a bytearray
by re-using an array of byte as storage initialised by
* the client.
*
* @param storage pre-initialised with desired value: the caller should not keep a reference
*/
public PyByteArray(byte[] storage) {
super(TYPE);
setStorage(storage);
}
/**
* Constructs a bytearray
by re-using an array of byte as storage initialised by
* the client.
*
* @param storage pre-initialised with desired value: the caller should not keep a reference
* @param size number of bytes actually used
* @throws IllegalArgumentException if the range [0:size] is not within the array bounds of the
* storage.
*/
public PyByteArray(byte[] storage, int size) {
super(TYPE);
setStorage(storage, size);
}
/**
* Constructs a new bytearray
object from an arbitrary Python object according to
* the same rules as apply in Python to the bytearray()
constructor:
*
* bytearray()
Construct a zero-length bytearray
.
* bytearray(int)
Construct a zero-initialized bytearray
of the
* given length.
* bytearray(iterable_of_ints)
Construct from iterable yielding integers in
* [0..255]
* bytearray(buffer)
Construct by reading from any object implementing
* {@link BufferProtocol}, including str/bytes
or another bytearray
.
*
* When it is necessary to specify an encoding, as in the Python signature
* bytearray(string, encoding [, errors])
, use the constructor
* {@link #PyByteArray(PyString, String, String)}. If the PyString
is actually a
* PyUnicode
, an encoding must be specified, and using this constructor will throw
* an exception about that.
*
* @param arg primary argument from which value is taken (may be null
)
* @throws PyException {@code TypeError} for non-iterable,
* @throws PyException {@code ValueError} if iterables do not yield byte [0..255] values.
*/
public PyByteArray(PyObject arg) throws PyException {
super(TYPE);
init(arg);
}
/*
* ============================================================================================
* Support for the Buffer API
* ============================================================================================
*
* The buffer API allows other classes to access the storage directly.
*/
/**
* Hold weakly a reference to a PyBuffer export not yet released, used to prevent untimely
* resizing.
*/
private WeakReference export;
/**
* {@inheritDoc}
*
* The {@link PyBuffer} returned from this method is a one-dimensional array of single byte
* items that allows modification of the object state. The existence of this export prohibits
* resizing the byte array. This prohibition is not only on the consumer of the view but
* extends to any other operations, such as any kind or insertion or deletion.
*/
@Override
public synchronized PyBuffer getBuffer(int flags) {
// If we have already exported a buffer it may still be available for re-use
BaseBuffer pybuf = getExistingBuffer(flags);
if (pybuf == null) {
// No existing export we can re-use: create a new one
pybuf = new SimpleWritableBuffer(flags, this, storage, offset, size);
// Hold a reference for possible re-use
export = new WeakReference(pybuf);
}
return pybuf;
}
/**
* Try to re-use an existing exported buffer, or return null
if we can't.
*
* @throws PyException {@code BufferError} if the the flags are incompatible with the buffer
*/
private BaseBuffer getExistingBuffer(int flags) throws PyException {
BaseBuffer pybuf = null;
if (export != null) {
// A buffer was exported at some time.
pybuf = export.get();
if (pybuf != null) {
/*
* We do not test for pybuf.isReleased() as, if any operation had taken place that
* invalidated the buffer, resizeCheck() would have set export=null. The exported
* buffer (navigation, buf member, etc.) remains valid through any operation that
* does not need a resizeCheck.
*/
pybuf = pybuf.getBufferAgain(flags);
}
}
return pybuf;
}
/**
* Test to see if the byte array may be resized and raise a BufferError if not. This must be
* called by the implementation of any append or insert that changes the number of bytes in the
* array.
*
* @throws PyException {@code BufferError} if there are buffer exports preventing a resize
*/
protected void resizeCheck() throws PyException {
if (export != null) {
// A buffer was exported at some time and we have not explicitly discarded it.
PyBuffer pybuf = export.get();
if (pybuf != null && !pybuf.isReleased()) {
// A consumer still has the exported buffer
throw Py.BufferError("Existing exports of data: object cannot be re-sized");
} else {
/*
* Either the reference has expired or all consumers have released it. Either way,
* the weak reference is useless now.
*/
export = null;
}
}
}
/*
* ============================================================================================
* API for org.python.core.PySequence
* ============================================================================================
*/
/**
* Returns a slice of elements from this sequence as a PyByteArray
.
*
* @param start the position of the first element.
* @param stop one more than the position of the last element.
* @param step the step size.
* @return a PyByteArray
corresponding the the given range of elements.
*/
@Override
protected synchronized PyByteArray getslice(int start, int stop, int step) {
if (step == 1) {
// Efficiently copy contiguous slice
return this.getslice(start, stop);
} else {
int n = sliceLength(start, stop, step);
PyByteArray ret = new PyByteArray(n);
n += ret.offset;
byte[] dst = ret.storage;
for (int io = start + offset, jo = ret.offset; jo < n; io += step, jo++) {
dst[jo] = storage[io];
}
return ret;
}
}
/**
* Specialisation of {@link #getslice(int, int, int)} to contiguous slices (of step size 1) for
* brevity and efficiency.
*/
@Override
protected synchronized PyByteArray getslice(int start, int stop) {
// If this were immutable, start==0 and end==size we would return (this).
// Efficiently copy contiguous slice
int n = stop - start;
if (n <= 0) {
return new PyByteArray();
} else {
PyByteArray ret = new PyByteArray(n);
System.arraycopy(storage, offset + start, ret.storage, ret.offset, n);
return ret;
}
}
/**
* Returns a PyByteArray
that repeats this sequence the given number of times, as
* in the implementation of __mul__ for strings.
*
* @param count the number of times to repeat this.
* @return this byte array repeated count times.
*/
@Override
protected synchronized PyByteArray repeat(int count) {
Builder builder = new Builder(size * (long) count);
builder.repeat(this, count);
return getResult(builder);
}
/**
* Replace the contents of this PyByteArray
with the given number of repeats of the
* original contents, as in the implementation of __mul__ for strings.
*
* @param count the number of times to repeat this.
*/
protected synchronized void irepeat(int count) {
// There are several special cases
if (size == 0 || count == 1) {
// No resize, so no check (consistent with CPython)
// Value is unchanged.
} else if (count <= 0) {
// Treat as if count == 0.
resizeCheck();
this.setStorage(emptyStorage);
} else {
// Open up space (remembering the original size)
int orginalSize = size;
storageExtend(orginalSize * (count - 1));
if (orginalSize == 1) {
// Do it efficiently for single bytes
byte b = storage[offset];
for (int i = 1, p = offset + 1; i < count; i++) {
storage[p++] = b;
}
} else {
// General case
for (int i = 1, p = offset + orginalSize; i < count; i++, p += orginalSize) {
System.arraycopy(storage, offset, storage, p, orginalSize);
}
}
}
}
/**
* Sets the indexed element of the bytearray
to the given value. This is an
* extension point called by PySequence in its implementation of {@link #__setitem__} It is
* guaranteed by PySequence that the index is within the bounds of the array. Any other clients
* calling pyset(int)
must make the same guarantee.
*
* @param index index of the element to set.
* @param value the value to set this element to.
* @throws PyException {@code AttributeError} if value cannot be converted to an integer
* @throws PyException {@code ValueError} if value<0 or value>255
*/
@Override
public synchronized void pyset(int index, PyObject value) throws PyException {
storage[index + offset] = byteCheck(value);
}
/**
* Insert the element (interpreted as a Python byte value) at the given index. Python
* int
, long
and str
types of length 1 are allowed.
*
* @param index to insert at
* @param element to insert (by value)
* @throws PyException {@code IndexError} if the index is outside the array bounds
* @throws PyException {@code ValueError} if element<0 or element>255
* @throws PyException {@code TypeError} if the subclass is immutable
*/
@Override
public synchronized void pyinsert(int index, PyObject element) {
// Open a space at the right location.
storageReplace(index, 0, 1);
storage[offset + index] = byteCheck(element);
}
/**
* Sets the given range of elements according to Python slice assignment semantics. If the step
* size is one, it is a simple slice and the operation is equivalent to deleting that slice,
* then inserting the value at that position, regarding the value as a sequence (if possible) or
* as a single element if it is not a sequence. If the step size is not one, but start=stop, it
* is equivalent to insertion at that point. If the step size is not one, and start!=stop, the
* slice defines a certain number of elements to be replaced, and the value must be a sequence
* of exactly that many elements (or convertible to such a sequence).
*
* When assigning from a sequence type or iterator, the sequence may contain arbitrary
* {@link PyObject}s, but acceptable ones are {@link PyInteger}, {@link PyLong} or
* {@link PyString} of length 1. If any one of them proves unsuitable for assignment to a Python
* bytearray
element, an exception is thrown and this bytearray
is
* unchanged.
*
*
* a = bytearray(b'abcdefghijklmnopqrst')
* a[2:12:2] = iter( [65, 66, 67, long(68), "E"] )
*
*
* Results in a=bytearray(b'abAdBfChDjElmnopqrst')
.
*
*
* @param start the position of the first element.
* @param stop one more than the position of the last element.
* @param step the step size.
* @param value an object consistent with the slice assignment
*/
@Override
protected synchronized void setslice(int start, int stop, int step, PyObject value) {
if (step == 1 && stop < start) {
// Because "b[5:2] = v" means insert v just before 5 not 2.
// ... although "b[5:2:-1] = v means b[5]=v[0], b[4]=v[1], b[3]=v[2]
stop = start;
}
/*
* The actual behaviour depends on the nature (type) of value. It may be any kind of
* PyObject (but not other kinds of Java Object). The function is implementing assignment to
* a slice. PEP 3137 declares that the value may be "any type that implements the PEP 3118
* buffer interface".
*
* The following is essentially equivalent to b[start:stop[:step]]=bytearray(value) except
* we avoid constructing a copy of value if we can easily do so. The logic is the same as
* BaseBytes.init(PyObject), without support for value==null.
*/
if (value instanceof PyString) {
/*
* Value is a string (but must be 8-bit).
*/
setslice(start, stop, step, (PyString)value);
} else if (value.isIndex()) {
/*
* Value behaves as a zero-initialised bytearray of the given length.
*/
setslice(start, stop, step, value.asIndex(Py.OverflowError));
} else if (value instanceof BaseBytes) {
/*
* Value behaves as a bytearray, and can be can be inserted without making a copy
* (unless it is this object).
*/
setslice(start, stop, step, (BaseBytes)value);
} else if (setsliceFromBuffer(start, stop, step, value)) {
// Value supports Jython buffer API. (We're done.)
} else {
/*
* The remaining alternative is an iterable returning (hopefully) right-sized ints. If
* it isn't one, we get an exception about not being iterable, or about the values.
*/
setslice(start, stop, step, value.asIterable());
}
}
/**
* Sets the given range of elements according to Python slice assignment semantics from a
* zero-filled bytearray
of the given length.
*
* @see #setslice(int, int, int, PyObject)
* @param start the position of the first element.
* @param stop one more than the position of the last element.
* @param step the step size.
* @param len number of zeros to insert consistent with the slice assignment
* @throws PyException {@code SliceSizeError} if the value size is inconsistent with an extended slice
*/
private void setslice(int start, int stop, int step, int len) throws PyException {
if (step == 1) {
// Delete this[start:stop] and open a space of the right size = len
storageReplace(start, stop - start, len);
Arrays.fill(storage, start + offset, (start + offset) + len, (byte)0);
} else {
// This is an extended slice which means we are replacing elements
int n = sliceLength(start, stop, step);
if (n != len) {
throw SliceSizeError("bytes", len, n);
}
for (int io = start + offset; n > 0; io += step, --n) {
storage[io] = 0;
}
}
}
/**
* Sets the given range of elements according to Python slice assignment semantics from a
* {@link PyString} that is not a {@link PyUnicode}.
*
* @see #setslice(int, int, int, PyObject)
* @param start the position of the first element.
* @param stop one more than the position of the last element.
* @param step the step size.
* @param value a PyString object consistent with the slice assignment
* @throws PyException {@code SliceSizeError} if the value size is inconsistent with an extended slice
* @throws PyException {@code ValueError} if the value is a PyUnicode
*/
private void setslice(int start, int stop, int step, PyString value) throws PyException {
if (value instanceof PyUnicode) {
// Has to be 8-bit PyString
throw Py.TypeError("can't set bytearray slice from unicode");
} else {
// Assignment is from 8-bit data
String v = value.asString();
int len = v.length();
if (step == 1) {
// Delete this[start:stop] and open a space of the right size
storageReplace(start, stop - start, len);
setBytes(start, v);
} else {
// This is an extended slice which means we are replacing elements
int n = sliceLength(start, stop, step);
if (n != len) {
throw SliceSizeError("bytes", len, n);
}
setBytes(start, step, v);
}
}
}
/**
* Sets the given range of elements according to Python slice assignment semantics from an
* object supporting the Jython implementation of PEP 3118.
*
* @see #setslice(int, int, int, PyObject)
* @param start the position of the first element.
* @param stop one more than the position of the last element.
* @param step the step size.
* @param value an object supporting the buffer API consistent with the slice assignment
* @throws PyException {@code SliceSizeError} if the value size is inconsistent with an extended slice
*/
private void setslice(int start, int stop, int step, BufferProtocol value) throws PyException, ClassCastException {
try (PyBuffer view = value.getBuffer(PyBUF.SIMPLE)) {
int len = view.getLen();
if (step == 1) {
// Delete this[start:stop] and open a space of the right size
storageReplace(start, stop - start, len);
view.copyTo(storage, start + offset);
} else {
// This is an extended slice which means we are replacing elements
int n = sliceLength(start, stop, step);
if (n != len) {
throw SliceSizeError("bytes", len, n);
}
for (int io = start + offset, j = 0; j < n; io += step, j++) {
storage[io] = view.byteAt(j); // Assign this[i] = value[j]
}
}
}
}
/**
* Sets the given range of elements according to Python slice assignment semantics from an
* object that might support the Jython Buffer API.
*
* @param start the position of the first element.
* @param stop one more than the position of the last element.
* @param step the step size.
* @param value an object possibly bearing the Buffer API
* @return true
if the slice was set successfully, false
otherwise
* @throws PyException {@code SliceSizeError} if the value size is inconsistent with an extended slice
*/
private boolean setsliceFromBuffer(int start, int stop, int step, PyObject value)
throws PyException {
if (value instanceof BufferProtocol) {
try {
setslice(start, stop, step, (BufferProtocol) value);
return true;
} catch (ClassCastException e) { /* fall through to false */ }
}
return false;
}
/**
* Sets the given range of elements according to Python slice assignment semantics from a
* bytearray
(or bytes).
*
* @see #setslice(int, int, int, PyObject)
* @param start the position of the first element.
* @param stop one more than the position of the last element.
* @param step the step size.
* @param value a bytearray
(or bytes) object consistent with the slice assignment
* @throws PyException {@code SliceSizeError} if the value size is inconsistent with an extended slice
*/
private void setslice(int start, int stop, int step, BaseBytes value) throws PyException {
if (value == this) { // Must work with a copy
value = new PyByteArray(value);
}
int len = value.size;
if (step == 1) {
// Delete this[start:stop] and open a space of the right size
storageReplace(start, stop - start, len);
System.arraycopy(value.storage, value.offset, storage, start + offset, len);
} else {
// This is an extended slice which means we are replacing elements
int n = sliceLength(start, stop, step);
if (n != len) {
throw SliceSizeError("bytes", len, n);
}
int no = n + value.offset;
for (int io = start + offset, jo = value.offset; jo < no; io += step, jo++) {
storage[io] = value.storage[jo]; // Assign this[i] = value[j]
}
}
}
/**
* Sets the given range of elements according to Python slice assignment semantics from a
* bytearray
(or bytes).
*
* @see #setslice(int, int, int, PyObject)
* @param start the position of the first element.
* @param stop one more than the position of the last element.
* @param step the step size.
* @param iter iterable source of values to enter in the array
* @throws PyException {@code SliceSizeError} if the iterable size is inconsistent with an
* extended slice
*/
private void setslice(int start, int stop, int step, Iterable extends PyObject> iter) {
/*
* As we don't know how many elements the iterable represents, we can't adjust the array
* until after we run the iterator. We use this elastic byte structure to hold the bytes
* until then.
*/
FragmentList fragList = new BaseBytes.FragmentList();
fragList.loadFrom(iter);
if (step == 1) {
// Delete this[start:stop] and open a space of the right size
storageReplace(start, stop - start, fragList.totalCount);
if (fragList.totalCount > 0) {
// Stitch the fragments together in the space we made
fragList.emptyInto(storage, start + offset);
}
} else {
// This is an extended slice which means we are replacing elements
int n = sliceLength(start, stop, step);
if (n != fragList.totalCount) {
throw SliceSizeError("bytes", fragList.totalCount, n);
}
fragList.emptyInto(storage, start + offset, step);
}
}
@Override
protected synchronized void del(int index) {
storageDelete(index, 1);
}
@Override
protected synchronized void delRange(int start, int stop) {
storageDelete(start, stop - start);
}
@Override
protected synchronized void delslice(int start, int stop, int step, int n) {
// This will not be possible if this object has buffer exports
resizeCheck();
if (step == 1) {
// Delete this[start:stop] and close up the space.
storageDelete(start, n);
} else if (step == -1) {
// Also a contiguous case, but start > stop.
storageDelete(stop + 1, n);
} else {
// This is an extended slice. We will be deleting n isolated elements.
int p, m;
// We delete by copying from high to low memory, whatever the sign of step.
if (step > 1) {
// The lowest numbered element to delete is x[start]
p = start;
m = step - 1;
} else {
// The lowest numbered element to delete is x[start+(n-1)*step]]
p = start + (n - 1) * step;
m = -1 - step;
}
// Offset p to be a storage index.
p += offset;
// Copy n-1 blocks blocks of m bytes, each time skipping the byte to be deleted.
for (int i = 1; i < n; i++) {
// Skip q over the one we are deleting
int q = p + i;
// Now copy the m elements that follow.
for (int j = 0; j < m; j++) {
storage[p++] = storage[q++];
}
}
// Close up the gap. Note that for the copy, p was adjusted by the storage offset.
storageDelete(p - offset, n);
}
}
/**
* Convenience method to build (but not throw) a ValueError
PyException with the
* message "attempt to assign {type} of size {valueSize} to extended slice of size {sliceSize}"
*
* @param valueType
* @param valueSize size of sequence being assigned to slice
* @param sliceSize size of slice expected to receive
* @return PyException (ValueError) as detailed
*/
public static PyException SliceSizeError(String valueType, int valueSize, int sliceSize) {
String fmt = "attempt to assign %s of size %d to extended slice of size %d";
return Py.ValueError(String.format(fmt, valueType, valueSize, sliceSize));
// XXX consider moving to SequenceIndexDelegate.java or somewhere else generic, even Py
}
/**
* Initialise a mutable bytearray
object from various arguments. This single
* initialisation must support:
*
* bytearray()
Construct a zero-length bytearray
.
* bytearray(int)
Construct a zero-initialized bytearray
of the
* given length.
* bytearray(iterable_of_ints)
Construct from iterable yielding integers in
* [0..255]
* bytearray(buffer)
Construct by reading from any object implementing
* {@link BufferProtocol}, including str/bytes
or another bytearray
.
* bytearray(string, encoding [, errors])
Construct from a
* str/bytes
, decoded using the system default encoding, and encoded to bytes using
* the specified encoding.
* bytearray(unicode, encoding [, errors])
Construct from a
* unicode
string, encoded to bytes using the specified encoding.
*
* Although effectively a constructor, it is possible to call __init__
on a 'used'
* object so the method does not assume any particular prior state.
*
* @param args argument array according to Jython conventions
* @param kwds Keywords according to Jython conventions
* @throws PyException {@code TypeError} for non-iterable,
* @throws PyException {@code ValueError} if iterables do not yield byte [0..255] values.
*/
@ExposedNew
@ExposedMethod(doc = BuiltinDocs.bytearray___init___doc)
final synchronized void bytearray___init__(PyObject[] args, String[] kwds) {
ArgParser ap = new ArgParser("bytearray", args, kwds, "source", "encoding", "errors");
PyObject arg = ap.getPyObject(0, null);
// If not null, encoding and errors must be PyString (or PyUnicode)
PyObject encoding = ap.getPyObjectByType(1, PyBaseString.TYPE, null);
PyObject errors = ap.getPyObjectByType(2, PyBaseString.TYPE, null);
/*
* This method and the related init()s are modelled on CPython (see
* Objects/bytearrayobject.c : bytes_init()) but reorganised somewhat to maximise re-use
* with the implementation of assignment to a slice, which essentially has to construct a
* bytearray from the right-hand side. Hopefully, it still tries the same things in the same
* order and fails in the same way.
*/
if (encoding != null || errors != null) {
/*
* bytearray(string [, encoding [, errors]]) Construct from a text string by encoding it
* using the specified encoding.
*/
if (arg == null || !(arg instanceof PyString)) {
throw Py.TypeError("encoding or errors without sequence argument");
}
init((PyString)arg, encoding, errors);
} else {
// Now construct from arbitrary object (or null)
init(arg);
}
}
/*
* ============================================================================================
* Support for Builder
* ============================================================================================
*
*/
@Override
protected PyByteArray getResult(Builder b) {
return new PyByteArray(b.getStorage(), b.getSize());
}
/*
* ============================================================================================
* Python API rich comparison operations
* ============================================================================================
*/
@Override
public PyObject __eq__(PyObject other) {
return basebytes___eq__(other);
}
@Override
public PyObject __ne__(PyObject other) {
return basebytes___ne__(other);
}
@Override
public PyObject __lt__(PyObject other) {
return basebytes___lt__(other);
}
@Override
public PyObject __le__(PyObject other) {
return basebytes___le__(other);
}
@Override
public PyObject __ge__(PyObject other) {
return basebytes___ge__(other);
}
@Override
public PyObject __gt__(PyObject other) {
return basebytes___gt__(other);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___eq___doc)
final synchronized PyObject bytearray___eq__(PyObject other) {
return basebytes___eq__(other);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___ne___doc)
final synchronized PyObject bytearray___ne__(PyObject other) {
return basebytes___ne__(other);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___lt___doc)
final synchronized PyObject bytearray___lt__(PyObject other) {
return basebytes___lt__(other);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___le___doc)
final synchronized PyObject bytearray___le__(PyObject other) {
return basebytes___le__(other);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___ge___doc)
final synchronized PyObject bytearray___ge__(PyObject other) {
return basebytes___ge__(other);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___gt___doc)
final synchronized PyObject bytearray___gt__(PyObject other) {
return basebytes___gt__(other);
}
/*
* ============================================================================================
* Python API for bytearray
* ============================================================================================
*/
@Override
public PyObject __add__(PyObject o) {
return bytearray___add__(o);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___add___doc)
final synchronized PyObject bytearray___add__(PyObject o) {
// Duplicate this buffer, but size it large enough to hold the sum
byte[] copy = new byte[size + o.__len__()];
System.arraycopy(storage, offset, copy, 0, size);
PyByteArray sum = new PyByteArray(copy, size);
// Concatenate the other buffer
return sum.bytearray___iadd__(o);
}
/**
* Returns the number of bytes actually allocated.
*/
public int __alloc__() {
return bytearray___alloc__();
}
@ExposedMethod(doc = BuiltinDocs.bytearray___alloc___doc)
final int bytearray___alloc__() {
return storage.length;
}
/**
* Equivalent to the standard Python __imul__
method, that for a byte array returns
* a new byte array containing the same thing n times.
*/
@Override
public PyObject __imul__(PyObject n) {
return bytearray___imul__(n);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___imul___doc)
final PyObject bytearray___imul__(PyObject n) {
if (!n.isIndex()) {
return null;
}
irepeat(n.asIndex(Py.OverflowError));
return this;
}
/**
* Equivalent to the standard Python __mul__
method, that for a byte array returns
* a new byte array containing the same thing n times.
*/
@Override
public PyObject __mul__(PyObject n) {
return bytearray___mul__(n);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___mul___doc)
final PyObject bytearray___mul__(PyObject n) {
if (!n.isIndex()) {
return null;
}
return repeat(n.asIndex(Py.OverflowError));
}
/**
* Equivalent to the standard Python __rmul__
method, that for a byte array returns
* a new byte array containing the same thing n times.
*/
@Override
public PyObject __rmul__(PyObject n) {
return bytearray___rmul__(n);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___rmul___doc)
final PyObject bytearray___rmul__(PyObject n) {
if (!n.isIndex()) {
return null;
}
return repeat(n.asIndex(Py.OverflowError));
}
/**
* Append a single byte to the end of the array.
*
* @param element the byte to append.
*/
public void append(byte element) {
// Open a space at the end.
storageExtend(1);
storage[offset + size - 1] = element;
}
/**
* Append a single element to the end of the array, equivalent to:
* s[len(s):len(s)] = o
. The argument must be a PyInteger, PyLong or string of
* length 1.
*
* @param element the item to append.
* @throws PyException {@code ValueError} if element<0 or element>255
*/
public void append(PyObject element) {
bytearray_append(element);
}
@ExposedMethod(doc = BuiltinDocs.bytearray_append_doc)
final synchronized void bytearray_append(PyObject element) {
// Insert at the end, checked for type and range
storageExtend(1);
storage[offset + size - 1] = byteCheck(element);
}
/**
* Implement to the standard Python __contains__ method, which in turn implements the
* in
operator.
*
* @param o the element to search for in this bytearray
.
* @return the result of the search.
**/
@Override
public boolean __contains__(PyObject o) {
return basebytes___contains__(o);
}
@ExposedMethod(doc = BuiltinDocs.bytearray___contains___doc)
final boolean bytearray___contains__(PyObject o) {
return basebytes___contains__(o);
}
@ExposedMethod(doc = BuiltinDocs.bytearray_decode_doc)
final PyObject bytearray_decode(PyObject[] args, String[] keywords) {
return basebytes_decode(args, keywords);
}
/**
* Java API equivalent of Python center(width)
: return the bytes centered in an
* array of length width
, padded by spaces. A copy of the original byte array is
* returned if width is less than this.size()
.
*
* @param width desired
* @return new byte array containing result
*/
public PyByteArray center(int width) {
return (PyByteArray)basebytes_center(width, " ");
}
/**
* Java API equivalent of Python center(width [, fillchar])
: return the bytes
* centered in an array of length width
. Padding is done using the specified
* fillchar (default is a space). A copy of the original byte array is returned if
* width
is less than this.size()
.
*
* @param width desired
* @param fillchar one-byte String to fill with, or null
implying space
* @return new byte array containing the result
*/
public PyByteArray center(int width, String fillchar) {
return (PyByteArray)basebytes_center(width, fillchar);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_center_doc)
final PyByteArray bytearray_center(int width, String fillchar) {
return (PyByteArray)basebytes_center(width, fillchar);
}
/**
* Implementation of Python count(sub)
. Return the number of non-overlapping
* occurrences of sub
in this byte array.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @return count of occurrences of sub within this byte array
*/
public int count(PyObject sub) {
return basebytes_count(sub, null, null);
}
/**
* Implementation of Python count( sub [, start ] )
. Return the number of
* non-overlapping occurrences of sub
in the range [start:].
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @return count of occurrences of sub within this byte array
*/
public int count(PyObject sub, PyObject start) {
return basebytes_count(sub, start, null);
}
/**
* Implementation of Python count( sub [, start [, end ]] )
. Return the number of
* non-overlapping occurrences of sub
in the range [start, end]. Optional arguments
* start
and end
(which may be null
or
* Py.None
) are interpreted as in slice notation.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @param end of slice to search
* @return count of occurrences of sub within this byte array
*/
public int count(PyObject sub, PyObject start, PyObject end) {
return basebytes_count(sub, start, end);
}
@ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_count_doc)
final int bytearray_count(PyObject sub, PyObject start, PyObject end) {
return basebytes_count(sub, start, end);
}
/**
* Implementation of Python endswith(suffix)
.
*
* When suffix
is of a type that may be treated as an array of bytes, return
* true
if and only if this bytearray
ends with the
* suffix
. suffix
can also be a tuple of suffixes to look for.
*
* @param suffix byte array to match, or object viewable as such, or a tuple of them
* @return true if and only if this bytearray
ends with the suffix (or one of them)
*/
public boolean endswith(PyObject suffix) {
return basebytes_starts_or_endswith(suffix, null, null, true);
}
/**
* Implementation of Python endswith( suffix [, start ] )
.
*
* When suffix
is of a type that may be treated as an array of bytes, return
* true
if and only if this bytearray
ends with the
* suffix
. suffix
can also be a tuple of suffixes to look for. With
* optional start
(which may be null
or Py.None
), define
* the effective bytearray
to be the slice [start:]
of this
* bytearray
.
*
* @param suffix byte array to match, or object viewable as such, or a tuple of them
* @param start of slice in this bytearray
to match
* @return true if and only if this[start:] ends with the suffix (or one of them)
*/
public boolean endswith(PyObject suffix, PyObject start) {
return basebytes_starts_or_endswith(suffix, start, null, true);
}
/**
* Implementation of Python endswith( suffix [, start [, end ]] )
.
*
* When suffix
is of a type that may be treated as an array of bytes, return
* true
if and only if this bytearray
ends with the
* suffix
. suffix
can also be a tuple of suffixes to look for. With
* optional start
and end
(which may be null
or
* Py.None
), define the effective bytearray
to be the slice
* [start:end]
of this bytearray
.
*
* @param suffix byte array to match, or object viewable as such, or a tuple of them
* @param start of slice in this bytearray
to match
* @param end of slice in this bytearray
to match
* @return true if and only if this[start:end] ends with the suffix (or one of them)
*/
public boolean endswith(PyObject suffix, PyObject start, PyObject end) {
return basebytes_starts_or_endswith(suffix, start, end, true);
}
@ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_endswith_doc)
final boolean bytearray_endswith(PyObject suffix, PyObject start, PyObject end) {
return basebytes_starts_or_endswith(suffix, start, end, true);
}
/**
* Implementation of Python expandtabs()
: return a copy of the byte array where all
* tab characters are replaced by one or more spaces, as {@link #expandtabs(int)} with a tab
* size of 8 characters.
*
* @return copy of this byte array with tabs expanded
*/
public PyByteArray expandtabs() {
return (PyByteArray)basebytes_expandtabs(8);
}
/**
* Implementation of Python expandtabs(tabsize)
: return a copy of the byte array
* where all tab characters are replaced by one or more spaces, depending on the current column
* and the given tab size. The column number is reset to zero after each newline occurring in
* the array. This treats other non-printing characters or escape sequences as regular
* characters.
*
* @param tabsize number of character positions between tab stops
* @return copy of this byte array with tabs expanded
*/
public PyByteArray expandtabs(int tabsize) {
return (PyByteArray)basebytes_expandtabs(tabsize);
}
@ExposedMethod(defaults = "8", doc = BuiltinDocs.bytearray_expandtabs_doc)
final PyByteArray bytearray_expandtabs(int tabsize) {
return (PyByteArray)basebytes_expandtabs(tabsize);
}
/**
* Append the elements in the argument sequence to the end of the array, equivalent to:
* s[len(s):len(s)] = o
. The argument must be a subclass of {@link BaseBytes} or an
* iterable type returning elements compatible with byte assignment.
*
* @param o the sequence of items to append to the list.
*/
public void extend(PyObject o) {
bytearray_extend(o);
}
@ExposedMethod(doc = BuiltinDocs.bytearray_extend_doc)
final synchronized void bytearray_extend(PyObject o) {
// Raise TypeError if the argument is not iterable
o.__iter__();
// Use the general method, assigning to the crack at the end of the array.
// Note this deals with all legitimate PyObject types including the case o==this.
setslice(size, size, 1, o);
}
/**
* Implementation of Python find(sub)
. Return the lowest index in the byte array
* where byte sequence sub
is found. Return -1 if sub
is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @return index of start of occurrence of sub within this byte array
*/
public int find(PyObject sub) {
return basebytes_find(sub, null, null);
}
/**
* Implementation of Python find( sub [, start ] )
. Return the lowest index in the
* byte array where byte sequence sub
is found, such that sub
is
* contained in the slice [start:]
. Return -1 if sub
is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @return index of start of occurrence of sub within this byte array
*/
public int find(PyObject sub, PyObject start) {
return basebytes_find(sub, start, null);
}
/**
* Implementation of Python find( sub [, start [, end ]] )
. Return the lowest index
* in the byte array where byte sequence sub
is found, such that sub
* is contained in the slice [start:end]
. Arguments start
and
* end
(which may be null
or Py.None
) are interpreted as
* in slice notation. Return -1 if sub
is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @param end of slice to search
* @return index of start of occurrence of sub within this byte array
*/
public int find(PyObject sub, PyObject start, PyObject end) {
return basebytes_find(sub, start, end);
}
@ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_find_doc)
final int bytearray_find(PyObject sub, PyObject start, PyObject end) {
return basebytes_find(sub, start, end);
}
/**
* Implementation of Python class method bytearray.fromhex(string)
, that returns .
* a new PyByteArray
with a value taken from a string of two-digit hexadecimal
* numbers. Spaces (but not whitespace in general) are acceptable around the numbers, not
* within. Non-hexadecimal characters or un-paired hex digits raise a ValueError
. *
* Example:
*
*
* bytearray.fromhex('B9 01EF') -> * bytearray(b'\xb9\x01\xef')."
*
*
* @param hex specification of the bytes
* @throws PyException {@code ValueError} if non-hex characters, or isolated ones, are
* encountered
*/
static PyByteArray fromhex(String hex) throws PyException {
return bytearray_fromhex(TYPE, hex);
}
@ExposedClassMethod(doc = BuiltinDocs.bytearray_fromhex_doc)
static PyByteArray bytearray_fromhex(PyType type, String hex) {
// I think type tells us the actual class but we always return exactly a bytearray
// PyObject ba = type.__call__();
PyByteArray result = new PyByteArray();
basebytes_fromhex(result, hex);
return result;
}
@ExposedMethod(doc = BuiltinDocs.bytearray___getitem___doc)
final synchronized PyObject bytearray___getitem__(PyObject index) {
// Let the SequenceIndexDelegate take care of it
return delegator.checkIdxAndGetItem(index);
}
@Override
public PyObject __iadd__(PyObject o) {
return bytearray___iadd__(o);
}
@ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytearray___iadd___doc)
final synchronized PyObject bytearray___iadd__(PyObject o) {
PyType oType = o.getType();
if (oType == TYPE) {
// Use the general method, specifying the crack at the end of the array.
// Note this deals with the case o==this.
setslice(size, size, 1, (BaseBytes)o);
} else if (oType == PyString.TYPE) {
// Will fail if somehow not 8-bit clean
setslice(size, size, 1, (PyString)o);
} else if (setsliceFromBuffer(size, size, 1, o)) {
// No-op setsliceFromBuffer has already done the work and if it returns true then were done.
// setsliceFromBuffer will return false for PyUnicode where the buffer cannot be obtained.
} else {
// Unsuitable type
throw ConcatenationTypeError(oType, TYPE);
}
return this;
}
/**
* Implementation of Python index(sub)
. Like {@link #find(PyObject)} but raise
* {@link Py#ValueError} if sub
is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @return index of start of occurrence of sub within this byte array
*/
public int index(PyObject sub) {
return bytearray_index(sub, null, null);
}
/**
* Implementation of Python index( sub [, start ] )
. Like
* {@link #find(PyObject,PyObject)} but raise {@link Py#ValueError} if sub
is not
* found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @return index of start of occurrence of sub within this byte array
*/
public int index(PyObject sub, PyObject start) {
return bytearray_index(sub, start, null);
}
/**
* This type is not hashable.
*
* @throws PyException {@code TypeError} as this type is not hashable.
*/
@Override
public int hashCode() throws PyException {
return bytearray___hash__();
}
@ExposedMethod(doc = BuiltinDocs.bytearray___hash___doc)
final int bytearray___hash__() {
throw Py.TypeError(String.format("unhashable type: '%.200s'", getType().fastGetName()));
}
/**
* Implementation of Python index( sub [, start [, end ]] )
. Like
* {@link #find(PyObject,PyObject,PyObject)} but raise {@link Py#ValueError} if sub
* is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @param end of slice to search
* @return index of start of occurrence of sub within this byte array
* @throws PyException ValueError if sub not found in byte array
*/
public int index(PyObject sub, PyObject start, PyObject end) throws PyException {
return bytearray_index(sub, start, end);
}
@ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_index_doc)
final int bytearray_index(PyObject sub, PyObject start, PyObject end) {
// Like find but raise a ValueError if not found
int pos = basebytes_find(sub, start, end);
if (pos < 0) {
throw Py.ValueError("subsection not found");
}
return pos;
}
/**
* Insert the argument element into the byte array at the specified index. Same as
* s[index:index] = [o] if index >= 0
.
*
* @param index the position where the element will be inserted.
* @param value the element to insert.
*/
public void insert(PyObject index, PyObject value) {
bytearray_insert(index, value);
}
@ExposedMethod(doc = BuiltinDocs.bytearray_insert_doc)
final synchronized void bytearray_insert(PyObject index, PyObject value) {
// XXX: do something with delegator instead?
pyinsert(boundToSequence(index.asIndex()), value);
}
//
// Character class operations
//
@ExposedMethod(doc = BuiltinDocs.bytearray_isalnum_doc)
final boolean bytearray_isalnum() {
return basebytes_isalnum();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_isalpha_doc)
final boolean bytearray_isalpha() {
return basebytes_isalpha();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_isdigit_doc)
final boolean bytearray_isdigit() {
return basebytes_isdigit();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_islower_doc)
final boolean bytearray_islower() {
return basebytes_islower();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_isspace_doc)
final boolean bytearray_isspace() {
return basebytes_isspace();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_istitle_doc)
final boolean bytearray_istitle() {
return basebytes_istitle();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_isupper_doc)
final boolean bytearray_isupper() {
return basebytes_isupper();
}
//
// Case transformations
//
@ExposedMethod(doc = BuiltinDocs.bytearray_capitalize_doc)
final PyByteArray bytearray_capitalize() {
return (PyByteArray)basebytes_capitalize();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_lower_doc)
final PyByteArray bytearray_lower() {
return (PyByteArray)basebytes_lower();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_swapcase_doc)
final PyByteArray bytearray_swapcase() {
return (PyByteArray)basebytes_swapcase();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_title_doc)
final PyByteArray bytearray_title() {
return (PyByteArray)basebytes_title();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_upper_doc)
final PyByteArray bytearray_upper() {
return (PyByteArray)basebytes_upper();
}
/**
* Implementation of Python join(iterable)
. Return a bytearray
which
* is the concatenation of the byte arrays in the iterable iterable
. The separator
* between elements is the byte array providing this method.
*
* @param iterable of byte array objects, or objects viewable as such.
* @return byte array produced by concatenation.
*/
public PyByteArray join(PyObject iterable) {
return bytearray_join(iterable);
}
@ExposedMethod(doc = BuiltinDocs.bytearray_join_doc)
final PyByteArray bytearray_join(PyObject iterable) {
return basebytes_join(iterable.asIterable());
}
@ExposedMethod(doc = BuiltinDocs.bytearray___len___doc)
final int bytearray___len__() {
return super.__len__();
}
/**
* Java API equivalent of Python ljust(width)
: return the bytes left justified in
* an array of length width
, padded by spaces. A copy of the original byte array is
* returned if width is less than this.size()
.
*
* @param width desired
* @return new byte array containing result
*/
public PyByteArray ljust(int width) {
return (PyByteArray)basebytes_ljust(width, " ");
}
/**
* Java API equivalent of Python ljust(width [, fillchar])
: return the bytes
* left-justified in an array of length width
. Padding is done using the specified
* fillchar (default is a space). A copy of the original byte array is returned if
* width
is less than this.size()
.
*
* @param width desired
* @param fillchar one-byte String to fill with, or null
implying space
* @return new byte array containing the result
*/
public PyByteArray ljust(int width, String fillchar) {
return (PyByteArray)basebytes_ljust(width, fillchar);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_ljust_doc)
final PyByteArray bytearray_ljust(int width, String fillchar) {
// If this was immutable and width<=this.size we could return (this).
return (PyByteArray)basebytes_ljust(width, fillchar);
}
/**
* Implementation of Python lstrip()
. Return a copy of the byte array with the
* leading whitespace characters removed.
*
* @return a byte array containing this value stripped of those bytes
*/
public PyByteArray lstrip() {
return bytearray_lstrip(null);
}
/**
* Implementation of Python lstrip(bytes)
*
* Return a copy of the byte array with the leading characters removed. The bytes argument is an
* object specifying the set of characters to be removed. If null
or
* None
, the bytes argument defaults to removing whitespace. The bytes argument is
* not a prefix; rather, all combinations of its values are stripped.
*
* @param bytes treated as a set of bytes defining what values to strip
* @return a byte array containing this value stripped of those bytes (at the left)
*/
public PyByteArray lstrip(PyObject bytes) {
return bytearray_lstrip(bytes);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_lstrip_doc)
final synchronized PyByteArray bytearray_lstrip(PyObject bytes) {
int left;
if (bytes == null || bytes == Py.None) {
// Find left bound of the slice that results from the stripping of whitespace
left = lstripIndex();
} else {
// Find left bound of the slice that results from the stripping of the specified bytes
ByteSet byteSet = new ByteSet(getViewOrError(bytes));
left = lstripIndex(byteSet);
}
return getslice(left, size);
}
@ExposedMethod(doc = BuiltinDocs.bytearray_partition_doc)
final PyTuple bytearray_partition(PyObject sep) {
return basebytes_partition(sep);
}
/**
* Remove and return the last element in the byte array.
*
* @return PyInteger representing the value
*/
public PyInteger pop() {
return bytearray_pop(-1);
}
/**
* Remove and return the n
th byte element in the array.
*
* @param i the index of the byte to remove and return.
* @return PyInteger representing the value
*/
public PyInteger pop(int i) {
return bytearray_pop(i);
}
@ExposedMethod(defaults = "-1", doc = BuiltinDocs.bytearray_pop_doc)
final synchronized PyInteger bytearray_pop(int i) {
if (size == 0) {
throw Py.IndexError("pop from empty list");
} else {
// Deal with slice interpretation of single index
if (i < 0) {
i += size;
}
// Use List.remove(int)
return remove(i);
}
}
@ExposedMethod(doc = BuiltinDocs.bytearray___reduce___doc)
final PyObject bytearray___reduce__() {
return basebytes___reduce__();
}
/**
* Remove the first occurrence of an element from the array, equivalent to:
* del s[s.index(x)]
, although x must be convertable to a single byte value. The
* argument must be a PyInteger, PyLong or string of length 1.
*
* @param o the value to remove from the list.
* @throws PyException ValueError if o not found in bytearray
*/
public void remove(PyObject o) throws PyException {
bytearray_remove(o);
}
@ExposedMethod(doc = BuiltinDocs.bytearray_remove_doc)
final synchronized void bytearray_remove(PyObject o) {
// Check and extract the value, and search for it.
byte b = byteCheck(o);
int pos = index(b);
// Not finding it is an error
if (pos < 0) {
throw Py.ValueError("value not found in bytearray");
} else {
storageDelete(pos, 1);
}
}
/**
* An implementation of Python replace( old, new )
, returning a
* PyByteArray
with all occurrences of sequence oldB
replaced by
* newB
.
*
* @param oldB sequence to find
* @param newB relacement sequence
* @return result of replacement as a new PyByteArray
*/
public PyByteArray replace(PyObject oldB, PyObject newB) {
return basebytes_replace(oldB, newB, -1);
}
/**
* An implementation of Python replace( old, new [, count ] )
, returning a
* PyByteArray
with all occurrences of sequence oldB
replaced by
* newB
. If the optional argument count
is given, only the first
* count
occurrences are replaced.
*
* @param oldB sequence to find
* @param newB relacement sequence
* @param maxcount maximum occurrences are replaced or all if maxcount < 0
* @return result of replacement as a new PyByteArray
*/
public PyByteArray replace(PyObject oldB, PyObject newB, int maxcount) {
return basebytes_replace(oldB, newB, maxcount);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_replace_doc)
final PyByteArray bytearray_replace(PyObject oldB, PyObject newB, PyObject count) {
int maxcount = (count == null) ? -1 : count.asInt(); // or count.asIndex() ?
return basebytes_replace(oldB, newB, maxcount);
}
/**
* Reverses the contents of the byte array in place. The reverse() methods modify in place for
* economy of space when reversing a large array. It doesn't return the reversed array to remind
* you that it works by side effect.
*/
public void reverse() {
bytearray_reverse();
}
@ExposedMethod(doc = BuiltinDocs.bytearray_reverse_doc)
final synchronized void bytearray_reverse() {
// In place reverse
int a = offset, b = offset + size;
while (--b > a) {
byte t = storage[b];
storage[b] = storage[a];
storage[a++] = t;
}
}
/**
* Implementation of Python rfind(sub)
. Return the highest index in the byte array
* where byte sequence sub
is found. Return -1 if sub
is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @return index of start of rightmost occurrence of sub within this byte array
*/
public int rfind(PyObject sub) {
return basebytes_rfind(sub, null, null);
}
/**
* Implementation of Python rfind( sub [, start ] )
. Return the highest index in
* the byte array where byte sequence sub
is found, such that sub
is
* contained in the slice [start:]
. Return -1 if sub
is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @return index of start of rightmost occurrence of sub within this byte array
*/
public int rfind(PyObject sub, PyObject start) {
return basebytes_rfind(sub, start, null);
}
/**
* Implementation of Python rfind( sub [, start [, end ]] )
. Return the highest
* index in the byte array where byte sequence sub
is found, such that
* sub
is contained in the slice [start:end]
. Arguments
* start
and end
(which may be null
or
* Py.None
) are interpreted as in slice notation. Return -1 if sub
is
* not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @param end of slice to search
* @return index of start of rightmost occurrence of sub within this byte array
*/
public int rfind(PyObject sub, PyObject start, PyObject end) {
return basebytes_rfind(sub, start, end);
}
@ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_rfind_doc)
final int bytearray_rfind(PyObject sub, PyObject start, PyObject end) {
return basebytes_rfind(sub, start, end);
}
/**
* Implementation of Python rindex(sub)
. Like {@link #find(PyObject)} but raise
* {@link Py#ValueError} if sub
is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @return index of start of occurrence of sub within this byte array
*/
public int rindex(PyObject sub) {
return bytearray_rindex(sub, null, null);
}
/**
* Implementation of Python rindex( sub [, start ] )
. Like
* {@link #find(PyObject,PyObject)} but raise {@link Py#ValueError} if sub
is not
* found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @return index of start of occurrence of sub within this byte array
*/
public int rindex(PyObject sub, PyObject start) {
return bytearray_rindex(sub, start, null);
}
/**
* Java API equivalent of Python rjust(width)
: return the bytes right justified in
* an array of length width
, padded by spaces. A copy of the original byte array is
* returned if width is less than this.size()
.
*
* @param width desired
* @return new byte array containing result
*/
public PyByteArray rjust(int width) {
return (PyByteArray)basebytes_rjust(width, " ");
}
/**
* Java API equivalent of Python rjust(width [, fillchar])
: return the bytes
* right-justified in an array of length width
. Padding is done using the specified
* fillchar (default is a space). A copy of the original byte array is returned if
* width
is less than this.size()
.
*
* @param width desired
* @param fillchar one-byte String to fill with, or null
implying space
* @return new byte array containing the result
*/
public PyByteArray rjust(int width, String fillchar) {
return (PyByteArray)basebytes_rjust(width, fillchar);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_rjust_doc)
final PyByteArray bytearray_rjust(int width, String fillchar) {
return (PyByteArray)basebytes_rjust(width, fillchar);
}
/**
* Implementation of Python rindex( sub [, start [, end ]] )
. Like
* {@link #find(PyObject,PyObject,PyObject)} but raise {@link Py#ValueError} if sub
* is not found.
*
* @param sub sequence to find (of a type viewable as a byte sequence)
* @param start of slice to search
* @param end of slice to search
* @return index of start of occurrence of sub within this byte array
*/
public int rindex(PyObject sub, PyObject start, PyObject end) {
return bytearray_rindex(sub, start, end);
}
@ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_rindex_doc)
final int bytearray_rindex(PyObject sub, PyObject start, PyObject end) {
// Like rfind but raise a ValueError if not found
int pos = basebytes_rfind(sub, start, end);
if (pos < 0) {
throw Py.ValueError("subsection not found");
}
return pos;
}
@ExposedMethod(doc = BuiltinDocs.bytearray_rpartition_doc)
final PyTuple bytearray_rpartition(PyObject sep) {
return basebytes_rpartition(sep);
}
@ExposedMethod(defaults = {"null", "-1"}, doc = BuiltinDocs.bytearray_rsplit_doc)
final PyList bytearray_rsplit(PyObject sep, int maxsplit) {
return basebytes_rsplit(sep, maxsplit);
}
/**
* Implementation of Python rstrip()
. Return a copy of the byte array with the
* trailing whitespace characters removed.
*
* @return a byte array containing this value stripped of those bytes (at right)
*/
public PyByteArray rstrip() {
return bytearray_rstrip(null);
}
/**
* Implementation of Python rstrip(bytes)
*
* Return a copy of the byte array with the trailing characters removed. The bytes argument is
* an object specifying the set of characters to be removed. If null
or
* None
, the bytes argument defaults to removing whitespace. The bytes argument is
* not a suffix; rather, all combinations of its values are stripped.
*
* @param bytes treated as a set of bytes defining what values to strip
* @return a byte array containing this value stripped of those bytes (at right)
*/
public PyByteArray rstrip(PyObject bytes) {
return bytearray_rstrip(bytes);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_rstrip_doc)
final synchronized PyByteArray bytearray_rstrip(PyObject bytes) {
int right;
if (bytes == null || bytes == Py.None) {
// Find right bound of the slice that results from the stripping of whitespace
right = rstripIndex();
} else {
// Find right bound of the slice that results from the stripping of the specified bytes
ByteSet byteSet = new ByteSet(getViewOrError(bytes));
right = rstripIndex(byteSet);
}
return getslice(0, right);
}
@ExposedMethod(defaults = {"null", "-1"}, doc = BuiltinDocs.bytearray_split_doc)
final PyList bytearray_split(PyObject sep, int maxsplit) {
return basebytes_split(sep, maxsplit);
}
@ExposedMethod(defaults = "false", doc = BuiltinDocs.bytearray_splitlines_doc)
final PyList bytearray_splitlines(boolean keepends) {
return basebytes_splitlines(keepends);
}
/**
* Implementation of Python startswith(prefix)
.
*
* When prefix
is of a type that may be treated as an array of bytes, return
* true
if and only if this bytearray
starts with the
* prefix
. prefix
can also be a tuple of prefixes to look for.
*
* @param prefix byte array to match, or object viewable as such, or a tuple of them
* @return true if and only if this bytearray
starts with the prefix (or one of
* them)
*/
public boolean startswith(PyObject prefix) {
return basebytes_starts_or_endswith(prefix, null, null, false);
}
/**
* Implementation of Python startswith( prefix [, start ] )
.
*
* When prefix
is of a type that may be treated as an array of bytes, return
* true
if and only if this bytearray
starts with the
* prefix
. prefix
can also be a tuple of prefixes to look for. With
* optional start
(which may be null
or Py.None
), define
* the effective bytearray
to be the slice [start:]
of this
* bytearray
.
*
* @param prefix byte array to match, or object viewable as such, or a tuple of them
* @param start of slice in this bytearray
to match
* @return true if and only if this[start:] starts with the prefix (or one of them)
*/
public boolean startswith(PyObject prefix, PyObject start) {
return basebytes_starts_or_endswith(prefix, start, null, false);
}
/**
* Implementation of Python startswith( prefix [, start [, end ]] )
.
*
* When prefix
is of a type that may be treated as an array of bytes, return
* true
if and only if this bytearray
starts with the
* prefix
. prefix
can also be a tuple of prefixes to look for. With
* optional start
and end
(which may be null
or
* Py.None
), define the effective bytearray
to be the slice
* [start:end]
of this bytearray
.
*
* @param prefix byte array to match, or object viewable as such, or a tuple of them
* @param start of slice in this bytearray
to match
* @param end of slice in this bytearray
to match
* @return true if and only if this[start:end] starts with the prefix (or one of them)
*/
public boolean startswith(PyObject prefix, PyObject start, PyObject end) {
return basebytes_starts_or_endswith(prefix, start, end, false);
}
@ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytearray_startswith_doc)
final boolean bytearray_startswith(PyObject prefix, PyObject start, PyObject end) {
return basebytes_starts_or_endswith(prefix, start, end, false);
}
/**
* Implementation of Python strip()
. Return a copy of the byte array with the
* leading and trailing whitespace characters removed.
*
* @return a byte array containing this value stripped of those bytes (left and right)
*/
public PyByteArray strip() {
return bytearray_strip(null);
}
/**
* Implementation of Python strip(bytes)
*
* Return a copy of the byte array with the leading and trailing characters removed. The bytes
* argument is anbyte arrayt specifying the set of characters to be removed. If
* null
or None
, the bytes argument defaults to removing whitespace.
* The bytes argument is not a prefix or suffix; rather, all combinations of its values are
* stripped.
*
* @param bytes treated as a set of bytes defining what values to strip
* @return a byte array containing this value stripped of those bytes (left and right)
*/
public PyByteArray strip(PyObject bytes) {
return bytearray_strip(bytes);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_strip_doc)
final synchronized PyByteArray bytearray_strip(PyObject bytes) {
int left, right;
if (bytes == null || bytes == Py.None) {
// Find bounds of the slice that results from the stripping of whitespace
left = lstripIndex();
// If we hit the end that time, no need to work backwards
right = (left == size) ? size : rstripIndex();
} else {
// Find bounds of the slice that results from the stripping of the specified bytes
ByteSet byteSet = new ByteSet(getViewOrError(bytes));
left = lstripIndex(byteSet);
// If we hit the end that time, no need to work backwards
right = (left == size) ? size : rstripIndex(byteSet);
}
return getslice(left, right);
}
@ExposedMethod(doc = BuiltinDocs.bytearray___setitem___doc)
final synchronized void bytearray___setitem__(PyObject index, PyObject value) {
// Let the SequenceIndexDelegate take care of it
delegator.checkIdxAndSetItem(index, value);
}
/**
* An overriding of the standard Java {@link #toString()} method, returning a printable
* expression of this byte array in the form bytearray(b'hello')
, where in the
* "inner string", any special characters are escaped to their well-known backslash equivalents
* or a hexadecimal escape. The built-in function repr()
is expected to call this
* method, and wraps the result in a Python str
.
*/
@Override
public String toString() {
return this.asString();
}
@Override
public PyString __repr__(){
return bytearray___repr__();
}
@ExposedMethod(names = {"__repr__"}, doc = BuiltinDocs.bytearray___repr___doc)
final synchronized PyString bytearray___repr__() {
return new PyString(basebytes_repr("bytearray(b", ")"));
}
/**
* An overriding of the {@link PyObject#__str__()} method, returning PyString
,
* where in the characters are simply those with a point-codes given in this byte array. The
* built-in function str()
is expected to call this method.
*/
@Override
public PyString __str__() {
return bytearray_str();
}
@ExposedMethod(names = {"__str__"}, doc = BuiltinDocs.bytearray___str___doc)
final PyString bytearray_str() {
return new PyString(this.asString());
}
/**
* Implementation of Python translate(table).
*
* Return a copy of the byte array where all bytes occurring in the optional argument
* deletechars
are removed, and the remaining bytes have been mapped through the
* given translation table, which must be of length 256.
*
* @param table length 256 translation table (of a type that may be regarded as a byte array)
* @return translated byte array
*/
public PyByteArray translate(PyObject table) {
return bytearray_translate(table, null);
}
/**
* Implementation of Python translate(table[, deletechars]).
*
* Return a copy of the byte array where all bytes occurring in the optional argument
* deletechars
are removed, and the remaining bytes have been mapped through the
* given translation table, which must be of length 256.
*
* You can use the Python maketrans()
helper function in the string
* module to create a translation table. For string objects, set the table argument to
* None
for translations that only delete characters:
*
* @param table length 256 translation table (of a type that may be regarded as a byte array)
* @param deletechars object that may be regarded as a byte array, defining bytes to delete
* @return translated byte array
*/
public PyByteArray translate(PyObject table, PyObject deletechars) {
return bytearray_translate(table, deletechars);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.bytearray_translate_doc)
final PyByteArray bytearray_translate(PyObject table, PyObject deletechars) {
// Work with the translation table (if there is one) as a PyBuffer view.
try (PyBuffer tab = getTranslationTable(table)) {
// Accumulate the result here
PyByteArray result = new PyByteArray();
// There are 4 cases depending on presence/absence of table and deletechars
if (deletechars != null) {
// Work with the deletion characters as a buffer too.
try (PyBuffer d = getViewOrError(deletechars)) {
// Use a ByteSet to express which bytes to delete
ByteSet del = new ByteSet(d);
int limit = offset + size;
if (tab == null) {
// No translation table, so we're just copying with omissions
for (int i = offset; i < limit; i++) {
int b = storage[i] & 0xff;
if (!del.contains(b)) {
result.append((byte)b);
}
}
} else {
// Loop over this byte array and write translated bytes to the result
for (int i = offset; i < limit; i++) {
int b = storage[i] & 0xff;
if (!del.contains(b)) {
result.append(tab.byteAt(b));
}
}
}
}
} else {
// No deletion set.
if (tab == null) {
// ... and no translation table either: just copy
result.extend(this);
} else {
int limit = offset + size;
// Loop over this byte array and write translated bytes to the result
for (int i = offset; i < limit; i++) {
int b = storage[i] & 0xff;
result.append(tab.byteAt(b));
}
}
}
return result;
}
}
/**
* Return a {@link PyBuffer} representing a translation table, or raise an exception if it is
* the wrong size. The caller is responsible for calling {@link PyBuffer#release()} on any
* returned buffer.
*
* @param table the translation table (or null
or {@link PyNone})
* @return the buffer view of the table or null if there is no table
* @throws PyException if the table is not exacltly 256 bytes long
*/
private PyBuffer getTranslationTable(PyObject table) throws PyException {
PyBuffer tab = null;
// Normalise the translation table to a View (if there is one).
if (table != null && table != Py.None) {
tab = getViewOrError(table);
if (tab.getLen() != 256) {
throw Py.ValueError("translation table must be 256 bytes long");
}
}
return tab;
}
/**
* Implementation of Python zfill(width):
return the numeric string left filled
* with zeros in a byte array of length width
. A sign prefix is handled correctly
* if it is in the first byte. A copy of the original is returned if width is less than the
* current size of the array.
*
* @param width desired
* @return left-filled byte array
*/
public PyByteArray zfill(int width) {
return (PyByteArray)basebytes_zfill(width);
}
@ExposedMethod(doc = BuiltinDocs.bytearray_zfill_doc)
final PyByteArray bytearray_zfill(int width) {
return (PyByteArray)basebytes_zfill(width);
}
/*
* ============================================================================================
* Manipulation of storage capacity
* ============================================================================================
*
* Here we add to the inherited variables defining byte storage, the methods necessary to resize
* it.
*/
/**
* Recommend a length for (but don't allocate) a new storage array, taking into account the
* current length and the number of bytes now needed. The returned value will always be at least
* as big as the argument (the length will always be sufficient). If the return value is equal
* to the present length, it is recommending no reallocation; otherwise, the return is either
* comfortably bigger than what is currently needed, or significantly smaller than currently
* allocated. This method embeds our policy for growing or shrinking the allocated array.
*
* @param needed number of bytes needed
* @return recommended amount to allocate
*/
private final int recLength(int needed) {
final int L = storage.length;
if (needed > L || needed * 2 < L) {
// Amount needed is a lot less than current length, or
// more space we have, so recommend the new ideal size.
return roundUp(needed);
} else {
// Amount needed is less than current length, but it doesn't justify reallocation
return L;
}
}
/**
* Allocate fresh storage for at least the requested number of bytes. Spare bytes are allocated
* evenly at each end of the new storage by choice of a new value for offset. If the size needed
* is zero, the "storage" allocated is the shared emptyStorage array.
*
* @param needed becomes the new value of this.size
*/
@Override
protected void newStorage(int needed) {
if (needed > 0) {
final int L = recLength(needed);
try {
byte[] s = new byte[L]; // guaranteed zero (by JLS 2ed para 4.5.5)
setStorage(s, needed, (L - needed) / 2);
} catch (OutOfMemoryError e) {
throw Py.MemoryError(e.getMessage());
}
} else {
setStorage(emptyStorage);
}
}
/**
* Delete d
elements at index a
and prepare to insert e
* elements there by moving aside the surrounding elements. The method manipulates the
* storage
array contents, size
and offset
. It will
* allocate a new array storage
if necessary, or if desirable for efficiency. If
* the initial storage looks like this:
*
*
* |- s -|
* |--f--|--------a--------|---d---|-----b-----|----------------|
*
*
* then after the call the (possibly new) storage looks like this:
*
*
* |- s' -|
* |----f'----|--------a--------|----e----|-----b-----|--------------|
*
*
* where the contents of regions of length a
and b=size-(a+d)
have
* been preserved, although probably moved, and the gap between them has been adjusted to the
* requested size.
*
* The effect on this PyByteArray
is that:
*
*
* this.offset = f'
* this.size = s' = a + e + b
*
*
* The method does not implement the Python repertoire of slice indices, or bound-check the
* sizes given, since code leading up to the call has done that.
*
* @param a index of hole in byte array
* @param d number to discard (will discard x[a,a+d-1])
* @param e size of hole to open (will be x[a, a+e-1])
*/
private void storageReplace(int a, int d, int e) {
final int b = size - (a + d); // Count of B section
final int c = e - d; // Change in overall size
if (c == 0) {
return;// Everything stays where it is.
} else if (c > 0 && b == 0) {
storageExtend(c); // This is really an extend/append operation
return;
}
// This will not be possible if this object has buffer exports
resizeCheck();
// Compute some handy points of reference
final int L = storage.length;
final int f = offset;
final int s2 = a + e + b; // Size of result s'
final int L2 = recLength(s2); // Length of storage for result
if (L2 == L) {
// The result will definitely fit into the existing array
if (a <= b) {
// It would be less copying if we moved A=x[:a] not B=x[-b:].
// If B is to stay where it is, it means A will land here:
final int f2 = f - c;
if (f2 >= 0) {
// ... which luckily is still inside the array
if (a > 0) {
System.arraycopy(storage, f, storage, f2, a);
}
offset = f2;
size = s2;
} else {
// ... which unfortunately is before the start of the array.
// We have to move both A and B within the existing array
newStorageAvoided(a, d, b, e);
}
} else /* a > b */{
// It would be less copying if we moved B=x[-b:] not A=x[:a]
// If A is to stay where it is, it means B will land here:
final int g2 = f + a + e;
if (g2 + b <= L) {
// ... which luckily leaves all of B inside the array
if (b > 0) {
System.arraycopy(storage, g2 - c, storage, g2, b);
}
// this.offset is unchanged
size = s2;
} else {
// ... which unfortunately runs beyond the end of the array.
// We have to move both A and B within the existing array
newStorageAvoided(a, d, b, e);
}
}
} else if (L2 > 0) {
// New storage (bigger or much smaller) is called for. Copy A and B to it.
newStorage(L2, a, d, b, e);
} else {
// We need no storage at all
setStorage(emptyStorage);
}
}
/**
* Use the existing storage but move two blocks within it to leave a gap of the required size.
* This is the strategy usually used when the array is still big enough to hold the required new
* value, but we can't leave either block fixed. If the initial storage looks like this:
*
*
* |-----f-----|--------a--------|---d---|----------b----------|----------|
*
*
* then after the call the storage looks like this:
*
*
* |- s' -|
* |--f'--|--------a--------|---------e---------|----------b----------|---|
*
*
* where the regions of length a
and b=size-(a+d)
have been preserved
* and the gap between them adjusted to specification. The new offset f' is chosen heuristically
* by the method to optimise the efficiency of repeated adjustment near either end of the array,
* e.g. repeated prepend or append operations. The effect on this PyByteArray
is
* that:
*
*
* this.offset = f'
* this.size = s' = a+e+b
*
*
* Arguments are not checked for validity at all. At call time, a, d, b, and e are
* non-negative and not all zero.
*
* @param a length of A
* @param d gap between A and B in current storage layout
* @param b length of B
* @param e gap between A and B in new storage layout.
*/
private void newStorageAvoided(int a, int d, int b, int e) {
// Compute some handy points of reference
final int f = offset;
final int g = f + a + d; // Location of B section x[-b]
final int s2 = a + e + b; // Size of result s'
// Choose the new offset f' to make prepend or append operations quicker.
// E.g. if insertion was near the end (b small) put most of the new space at the end.
int f2;
if (a == b) {
// Mainly to trap the case a=b=0
f2 = (storage.length - s2) / 2;
} else {
// a and b are not both zero (since not equal)
long spare = storage.length - s2;
f2 = (int)((spare * b) / (a + b));
}
// This puts B at
final int g2 = f2 + a + e;
// We can make do with the existing array. Do an in place copy.
if (f2 + a > g) {
// New A overlaps existing B so we must copy B first
if (b > 0) {
System.arraycopy(storage, g, storage, g2, b);
}
if (a > 0) {
System.arraycopy(storage, f, storage, f2, a);
}
} else {
// Safe to copy A first
if (a > 0) {
System.arraycopy(storage, f, storage, f2, a);
}
if (b > 0) {
System.arraycopy(storage, g, storage, g2, b);
}
}
// We have a new size and offset (but the same storage)
size = s2;
offset = f2;
}
/**
* Allocate new storage and copy two blocks from the current storage to it. If the initial
* storage looks like this:
*
*
* |--f--|--------a--------|---d---|-----b-----|----------------|
*
*
* then after the call the (definitely new) storage looks like this:
*
*
* |- s' -|
* |----f'----|--------a--------|----e----|-----b-----|--------------|
*
*
* where the regions of length a
and b=size-(a+d)
have been preserved
* and the gap between them adjusted to specification. The new offset f' is chosen heuristically
* by the method to optimise the efficiency of repeated adjustment near either end of the array,
* e.g. repeated prepend or append operations. The effect on this PyByteArray
is
* that:
*
*
* this.offset = f'
* this.size = s' = a+e+b
*
*
* Arguments are not checked for validity at all. At call time, a, e and b are
* non-negative and not all zero.
*
* @param L2 length of storage array to allocate (decided by caller)
* @param a length of A
* @param d gap between A and B in current storage layout
* @param b length of B
* @param e gap between A and B in new storage layout.
*/
private void newStorage(int L2, int a, int d, int b, int e) {
// Compute some handy points of reference
final int f = offset;
final int g = f + a + d; // Location of B section x[-b]
final int s2 = a + e + b; // Size of result s'
// New storage as specified by caller
byte[] newStorage = new byte[L2];
// Choose the new offset f' to make repeated prepend or append operations quicker.
// E.g. if insertion was near the end (b small) put most of the new space at the end.
int f2;
if (a == b) {
// Mainly to trap the case a=b=0
f2 = (L2 - s2) / 2;
} else {
// a and b are not both zero (since not equal)
long spare = L2 - s2;
f2 = (int)((spare * b) / (a + b));
}
// Copy across the data from existing to new storage.
if (a > 0) {
System.arraycopy(storage, f, newStorage, f2, a);
}
if (b > 0) {
System.arraycopy(storage, g, newStorage, f2 + a + e, b);
}
setStorage(newStorage, s2, f2);
}
/**
* Prepare to insert e
elements at the end of the storage currently in use. If
* necessary. existing elements will be moved. The method manipulates the storage
* array contents, size
and offset
. It will allocate a new array
* storage
if necessary for growth. If the initial storage looks like this:
*
*
* |- s -|
* |------f------|-----------a-----------|-----------|
*
*
* then after the call the storage looks like this:
*
*
* |- s' -|
* |------f------|-----------a-----------|---e---|---|
*
*
* or like this if e was too big for the gap, but not enough to provoke reallocation:
*
*
* |- s' -|
* |-----------a-----------|--------e--------|-------|
*
*
* or like this if was necessary to allocate more storage:
*
*
* |- s' -|
* |-----------a-----------|-------------e-------------|--------------|
*
*
* where the contents of region a
have been preserved, although possbly moved, and
* the gap at the end has the requested size. this method never shrinks the total storage. The
* effect on this PyByteArray
is that:
*
*
* this.offset = f or 0
* this.size = s' = a + e
*
*
* The method does not implement the Python repertoire of slice indices, or bound-check the
* sizes given, since code leading up to the call has done that.
*
* @param e size of hole to open (will be x[a, a+e-1]) where a = size before call
*/
private void storageExtend(int e) {
// XXX Do a better job here or where called of checking offset+size+e <= storage.length
if (e == 0) {
return; // Everything stays where it is.
}
// This will not be possible if this object has buffer exports
resizeCheck();
// Compute some handy points of reference
final int L = storage.length;
final int f = offset;
final int s2 = size + e; // Size of result s'
final int L2 = recLength(s2); // Length of storage for result
if (L2 <= L) {
// Ignore recommendations to shrink and use the existing array
// If A is to stay where it is, it means E will end here:
final int g2 = f + s2;
if (g2 > L) {
// ... which unfortunately runs beyond the end of the array.
// We have to move A within the existing array to make room
if (size > 0) {
System.arraycopy(storage, offset, storage, 0, size);
}
offset = 0;
}
// New size
size = s2;
} else {
// New storage size as recommended
byte[] newStorage = new byte[L2];
// Choose the new offset f'=0 to make repeated append operations quicker.
// Copy across the data from existing to new storage.
if (size > 0) {
System.arraycopy(storage, f, newStorage, 0, size);
}
setStorage(newStorage, s2);
}
}
/**
* Delete d
elements at index a
by moving together the surrounding
* elements. The method manipulates the storage
array, size
and
* offset
, and will allocate a new storage array if necessary, or if the deletion
* is big enough. If the initial storage looks like this:
*
*
* |- L -|
* |- s -|
* |--f--|--------a--------|---d---|-----b-----|----------------|
*
*
* then after the call the (possibly new) storage looks like this:
*
*
* |- L' -|
* |- s' -|
* |-f'-|--------a--------|-----b-----|-------|
*
*
* where the regions of length a
and b=size-(a+d)
have been preserved
* and the gap between them eliminated. The effect on this PyByteArray
is that:
*
*
* this.offset = f'
* this.size = s' = a+b
*
*
* The method does not implement the Python repertoire of slice indices but avoids indexing
* outside the bytearray
by silently adjusting a to be within it. Negative d is
* treated as 0 and if d is too large, it is truncated to the array end.
*
* @param a index of hole in byte array
* @param d number to discard (will discard x[a,a+d-1])
* @param e size of hole to open (will be x[a, a+e-1])
*/
private void storageDelete(int a, int d) {
// storageReplace specialised for delete (e=0)
if (d == 0) {
return; // Everything stays where it is.
}
// This will not be possible if this object has buffer exports
resizeCheck();
// Compute some handy points of reference
final int L = storage.length;
final int f = offset;
final int b = size - (a + d); // Count of B section
final int s2 = a + b; // Size of result s'
final int L2 = recLength(s2); // Length of storage for result
if (L2 == L) {
// We are re-using the existing array
if (a <= b) {
// It would be less copying if we moved A=x[:a] not B=x[-b:].
// If B is to stay where it is, it means A will land here:
final int f2 = f + d;
if (a > 0) {
System.arraycopy(storage, f, storage, f2, a);
}
offset = f2;
size = s2;
} else /* a > b */{
// It would be less copying if we moved B=x[-b:] not A=x[:a]
// If A is to stay where it is, it means B will land here:
final int g2 = f + a;
if (b > 0) {
System.arraycopy(storage, g2 + d, storage, g2, b);
}
// this.offset is unchanged
size = s2;
}
} else if (L2 > 0) {
// New storage (much smaller) is called for. Copy A and B to it.
final int g = f + a + d; // Location of B section x[-b]
// Choose the new offset f' to distribute space evenly.
int f2 = (L2 - s2) / 2;
// New storage as specified
byte[] newStorage = new byte[L2];
// Copy across the data from existing to new storage.
if (a > 0) {
System.arraycopy(storage, f, newStorage, f2, a);
}
if (b > 0) {
System.arraycopy(storage, g, newStorage, f2 + a, b);
}
setStorage(newStorage, s2, f2);
} else {
// Everything must go
setStorage(emptyStorage);
}
}
}