diff --git a/CoreExposed.includes b/CoreExposed.includes index 8441adfe6..46944d4cb 100644 --- a/CoreExposed.includes +++ b/CoreExposed.includes @@ -7,6 +7,7 @@ org/python/core/PyBaseException.class org/python/core/PyBoolean.class org/python/core/PyBuiltinCallable.class org/python/core/PyByteArray.class +org/python/core/PyBytes.class org/python/core/PyCell.class org/python/core/PyClass.class org/python/core/PyClassMethod.class @@ -38,6 +39,7 @@ org/python/core/PyModule.class org/python/core/PyNone.class org/python/core/PyObject.class org/python/core/PyProperty.class +org/python/core/PyRange.class org/python/core/PySet.class org/python/core/PySlice.class org/python/core/PySlot.class @@ -49,7 +51,6 @@ org/python/core/PyTraceback.class org/python/core/PyTuple.class org/python/core/PyType.class org/python/core/PyUnicode.class -org/python/core/PyRange.class org/python/core/stringlib/MarkupIterator.class org/python/core/stringlib/FieldNameIterator.class org/python/modules/PyStruct.class diff --git a/src/org/python/compiler/CodeCompiler.java b/src/org/python/compiler/CodeCompiler.java index 85f95f749..38b330833 100644 --- a/src/org/python/compiler/CodeCompiler.java +++ b/src/org/python/compiler/CodeCompiler.java @@ -2604,10 +2604,13 @@ public Object visitName(Name node) throws Exception { @Override public Object visitStr(Str node) throws Exception { PyString s = (PyString)node.getInternalS(); + // TODO: in Python 3 there is no alteration here anymore - + // Bytes node ought to be used for the b'' strings if (s instanceof PyUnicode) { module.unicodeConstant(s.asString()).get(code); } else { - module.stringConstant(s.asString()).get(code); + // TODO: this ought to be in Bytes visit + module.bytesConstant(s.asString()).get(code); } return null; } diff --git a/src/org/python/compiler/Module.java b/src/org/python/compiler/Module.java index 5a5180623..c5cddb6f7 100644 --- a/src/org/python/compiler/Module.java +++ b/src/org/python/compiler/Module.java @@ -26,6 +26,7 @@ import org.python.core.CodeLoader; import org.python.core.CompilerFlags; import org.python.core.Py; +import org.python.core.PyBytes; import org.python.core.PyCode; import org.python.core.PyComplex; import org.python.core.PyException; @@ -145,7 +146,38 @@ public boolean equals(Object o) { } } +class PyBytesConstant extends Constant implements ClassConstants, Opcodes { + final String value; + + PyBytesConstant(String value) { + this.value = value; + } + + @Override + void get(Code c) throws IOException { + c.ldc(value); + c.invokestatic(p(PyBytes.class), "fromStringConstant", sig(PyBytes.class, String.class)); + } + @Override + void put(Code c) throws IOException {} + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof PyBytesConstant) { + return ((PyBytesConstant)o).value.equals(value); + } else { + return false; + } + } +} + +// TODO: remove class PyStringConstant extends Constant implements ClassConstants, Opcodes { final String value; @@ -487,6 +519,10 @@ Constant stringConstant(String value) { return findConstant(new PyStringConstant(value)); } + Constant bytesConstant(String value) { + return findConstant(new PyBytesConstant(value)); + } + Constant unicodeConstant(String value) { return findConstant(new PyUnicodeConstant(value)); } diff --git a/src/org/python/core/BaseBytes.java b/src/org/python/core/BaseBytes.java index c0baae451..a01182e86 100644 --- a/src/org/python/core/BaseBytes.java +++ b/src/org/python/core/BaseBytes.java @@ -7,6 +7,9 @@ import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.function.Supplier; + +import org.python.core.util.Allocator; /** * Base class for Jython bytearray (and bytes in due course) that provides @@ -1908,7 +1911,8 @@ private static int hexDigit(char c) throws IllegalArgumentException { * @param iter iterable of objects capable of being regarded as byte arrays * @return the byte array that is their join */ - final synchronized PyByteArray basebytes_join(Iterable iter) { + final synchronized T basebytes_join( + Iterable iter, Allocator allocator) { List iterList = new LinkedList(); long mysize = this.size; @@ -1941,7 +1945,7 @@ final synchronized PyByteArray basebytes_join(Iterable iter) } // Load the Views from the iterator into a new PyByteArray - PyByteArray result = new PyByteArray((int)totalSize); + T result = allocator.allocate((int)totalSize); int p = result.offset; // Copy-to pointer first = true; diff --git a/src/org/python/core/Py.java b/src/org/python/core/Py.java index a5857e716..3cf7f719b 100644 --- a/src/org/python/core/Py.java +++ b/src/org/python/core/Py.java @@ -109,6 +109,8 @@ private Object readResolve() throws ObjectStreamException { public final static PyBoolean True = new PyBoolean(true); /** A zero-length Python byte string **/ public final static PyString EmptyString = new PyString(""); + /** A zero-length Python bytes **/ + public final static PyBytes EmptyBytes = new PyBytes(); /** A zero-length Python Unicode string **/ public final static PyUnicode EmptyUnicode = new PyUnicode(""); /** A Python string containing '\n' **/ diff --git a/src/org/python/core/PyByteArray.java b/src/org/python/core/PyByteArray.java index 1023a6e7b..f9c09b7b8 100644 --- a/src/org/python/core/PyByteArray.java +++ b/src/org/python/core/PyByteArray.java @@ -5,6 +5,7 @@ import org.python.core.buffer.BaseBuffer; import org.python.core.buffer.SimpleWritableBuffer; +import org.python.core.util.Allocator; import org.python.expose.ExposedClassMethod; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; @@ -1495,7 +1496,12 @@ public PyByteArray join(PyObject iterable) { @ExposedMethod(doc = BuiltinDocs.bytearray_join_doc) final PyByteArray bytearray_join(PyObject iterable) { - return basebytes_join(iterable.asIterable()); + return basebytes_join(iterable.asIterable(), new Allocator() { + @Override + public PyByteArray allocate(int size) { + return new PyByteArray(size); + } + }); } @ExposedMethod(doc = BuiltinDocs.bytearray___len___doc) diff --git a/src/org/python/core/PyBytes.java b/src/org/python/core/PyBytes.java new file mode 100644 index 000000000..3d5841790 --- /dev/null +++ b/src/org/python/core/PyBytes.java @@ -0,0 +1,1541 @@ +package org.python.core; + +import java.lang.ref.WeakReference; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import org.python.core.buffer.BaseBuffer; +import org.python.core.buffer.SimpleBuffer; +import org.python.core.buffer.SimpleWritableBuffer; +import org.python.core.util.Allocator; +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 bytes 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 "bytes" or + * "object viewable as bytes" instead of bytes 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 = "bytes", base = PyObject.class, doc = BuiltinDocs.bytes_doc) +public class PyBytes extends BaseBytes implements BufferProtocol { + + /** The {@link PyType} of bytes. */ + public static final PyType TYPE = PyType.fromClass(PyBytes.class); + + /** + * Constructs a zero-length Python bytes of explicitly-specified sub-type + * + * @param type explicit Jython type + */ + public PyBytes(PyType type) { + super(type); + } + + /** + * Constructs a zero-length Python bytes. + */ + public PyBytes() { + super(TYPE); + } + + /** + * Constructs zero-filled Python bytes of specified size. + * + * @param size of bytes + */ + public PyBytes(int size) { + super(TYPE); + init(size); + } + + /** + * Constructs a bytes by copying values from int[]. + * + * @param value source of the bytes (and size) + */ + public PyBytes(int[] value) { + super(TYPE, value); + } + + /** + * Constructs a new array filled exactly by a copy of the contents of the source, which is a + * bytes (or bytes). + * + * @param value source of the bytes (and size) + */ + public PyBytes(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) + */ + PyBytes(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 PyBytes(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 PyBytes(Iterable 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 PyBytes(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 PyBytes(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 PyBytes(PyString arg) { + super(TYPE); + init(arg, (String)null, (String)null); + } + + /** + * Constructs a bytes 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 PyBytes(byte[] storage) { + super(TYPE); + setStorage(storage); + } + + /** + * Constructs a bytes 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 PyBytes(byte[] storage, int size) { + super(TYPE); + setStorage(storage, size); + } + + /** + * Constructs a new bytes object from an arbitrary Python object according to + * the same rules as apply in Python to the bytes() constructor: + *
    + *
  • bytes() Construct a zero-length bytes.
  • + *
  • bytes(int) Construct a zero-initialized bytes of the + * given length.
  • + *
  • bytes(iterable_of_ints) Construct from iterable yielding integers in + * [0..255]
  • + *
  • bytes(buffer) Construct by reading from any object implementing + * {@link BufferProtocol}, including str/bytes or another bytes.
  • + *
+ * When it is necessary to specify an encoding, as in the Python signature + * bytes(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 (TypeError) for non-iterable, + * @throws PyException (ValueError) if iterables do not yield byte [0..255] values. + */ + public PyBytes(PyObject arg) throws PyException { + super(TYPE); + init(arg); + } + + public PyBytes(PyType type, PyBytes contents) { + super(TYPE); + init((BaseBytes)contents); + } + + /** + * The bytes literals stored in .class files are actually stored as strings in + * fake ISO 8859-1 coding; this function will convert such a string to a bytes + * @param constant the string constant + * @return a newly created bytes + */ + public static PyBytes fromStringConstant(String constant) { + return new PyBytes(constant.getBytes(StandardCharsets.ISO_8859_1)); + } + + /* + * ============================================================================================ + * 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 bytes. 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 SimpleBuffer(flags, 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 (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; + } + + /* + * ============================================================================================ + * 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 PyBytes 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); + PyBytes ret = new PyBytes(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 PyBytes 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 PyBytes(); + } else { + PyBytes ret = new PyBytes(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 bytes repeated count times. + */ + @Override + protected synchronized PyBytes repeat(int count) { + PyBytes ret = new PyBytes(); + ret.setStorage(repeatImpl(count)); + return ret; + } + + /** + * Raise error for pyset + * + * @param index index of the element to set. + * @param value the value to set this element to. + * @throws PyException (AttributeError) if value cannot be converted to an integer + * @throws PyException (ValueError) if value<0 or value>255 + */ + @Override + public synchronized void pyset(int index, PyObject value) throws PyException { + throw Py.TypeError(String.format("'%s' object does not support item assignment", getType() + .fastGetName())); + } + + @Override + protected synchronized void del(int index) { + throw Py.TypeError(String.format("'%s' object does not support item deletion", getType() + .fastGetName())); + } + + @Override + protected synchronized void delRange(int start, int stop) { + throw Py.TypeError(String.format("'%s' object does not support item deletion", getType() + .fastGetName())); + } + + @Override + protected synchronized void delslice(int start, int stop, int step, int n) { + throw Py.TypeError(String.format("'%s' object does not support item deletion", getType() + .fastGetName())); + } + + /** + * Initialise a mutable bytes object from various arguments. This single + * initialisation must support: + *

    + *
  • bytes() Construct a zero-length bytes.
  • + *
  • bytes(int) Construct a zero-initialized bytes of the + * given length.
  • + *
  • bytes(iterable_of_ints) Construct from iterable yielding integers in + * [0..255]
  • + *
  • bytes(buffer) Construct by reading from any object implementing + * {@link BufferProtocol}, including str/bytes or another bytes.
  • + *
  • bytes(string, encoding [, errors]) Construct from a + * str/bytes, decoded using the system default encoding, and encoded to bytes using + * the specified encoding.
  • + *
  • bytes(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 + * @return + * @throws PyException (TypeError) for non-iterable, + * @throws PyException (ValueError) if iterables do not yield byte [0..255] values. + */ + @ExposedNew + static PyObject bytes___new__( + PyNewWrapper new_, boolean init, PyType subtype, + PyObject[] args, String[] kwds) + { + ArgParser ap = new ArgParser("bytes", 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); + + PyBytes value; + if (encoding != null || errors != null) { + /* + * bytes(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"); + } + + value = new PyBytes((PyString)arg, encoding, errors); + } + else { + value = new PyBytes(arg); + } + + if (new_.for_type == subtype) { + return value; + } else { + return new PyBytesDerived(subtype, value); + } + } + + /* + * ============================================================================================ + * Support for Builder + * ============================================================================================ + * + * Extend BaseBytes.Builder so that it can return a PyByteArray and give the superclass a hook + * for it. + */ + + @Override + protected Builder getBuilder(int capacity) { + // Return a Builder specialised for my class + return new Builder(capacity) { + + @Override + PyBytes getResult() { + // Create a PyByteArray from the storage that the builder holds + return new PyBytes(getStorage(), 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.bytes___eq___doc) + final synchronized PyObject bytes___eq__(PyObject other) { + return basebytes___eq__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytes___ne___doc) + final synchronized PyObject bytes___ne__(PyObject other) { + return basebytes___ne__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytes___lt___doc) + final synchronized PyObject bytes___lt__(PyObject other) { + return basebytes___lt__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytes___le___doc) + final synchronized PyObject bytes___le__(PyObject other) { + return basebytes___le__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytes___ge___doc) + final synchronized PyObject bytes___ge__(PyObject other) { + return basebytes___ge__(other); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytes___gt___doc) + final synchronized PyObject bytes___gt__(PyObject other) { + return basebytes___gt__(other); + } + +/* + * ============================================================================================ + * Python API for bytes + * ============================================================================================ + */ + + @Override + public PyObject __add__(PyObject o) { + return bytes___add__(o); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytes___add___doc) + final synchronized PyObject bytes___add__(PyObject o) { + PyBytes sum = null; + + // XXX re-write using buffer API + + if (o instanceof BaseBytes) { + BaseBytes ob = (BaseBytes)o; + // Quick route: allocate the right size bytes and copy the two parts in. + sum = new PyBytes(size + ob.size); + System.arraycopy(storage, offset, sum.storage, sum.offset, size); + System.arraycopy(ob.storage, ob.offset, sum.storage, sum.offset + size, ob.size); + + } else { + // Unsuitable type + // XXX note reversed order relative to __iadd__ may be wrong, matches Python 2.7 + throw ConcatenationTypeError(TYPE, o.getType()); + } + + return sum; + } + + /** + * Equivalent to the standard Python __mul__ method, that for a bytes returns + * a new bytes containing the same thing n times. + */ + @Override + public PyObject __mul__(PyObject n) { + return bytes___mul__(n); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytes___mul___doc) + final PyObject bytes___mul__(PyObject n) { + if (!n.isIndex()) { + return null; + } + return repeat(n.asIndex(Py.OverflowError)); + } + + /** + * Equivalent to the standard Python __rmul__ method, that for a bytes returns + * a new bytes containing the same thing n times. + */ + @Override + public PyObject __rmul__(PyObject n) { + return bytes___rmul__(n); + } + + @ExposedMethod(type = MethodType.BINARY, doc = BuiltinDocs.bytes___rmul___doc) + final PyObject bytes___rmul__(PyObject n) { + if (!n.isIndex()) { + return null; + } + return repeat(n.asIndex(Py.OverflowError)); + } + + /** + * Implement to the standard Python __contains__ method, which in turn implements the + * in operator. + * + * @param o the element to search for in this bytes. + * @return the result of the search. + **/ + @Override + public boolean __contains__(PyObject o) { + return basebytes___contains__(o); + } + + @ExposedMethod(doc = BuiltinDocs.bytes___contains___doc) + final boolean bytes___contains__(PyObject o) { + return basebytes___contains__(o); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_decode_doc) + final PyObject bytes_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 bytes is + * returned if width is less than this.size(). + * + * @param width desired + * @return new bytes containing result + */ + public PyBytes center(int width) { + return (PyBytes)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 bytes 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 bytes containing the result + */ + public PyBytes center(int width, String fillchar) { + return (PyBytes)basebytes_center(width, fillchar); + } + + @ExposedMethod(defaults = "null", doc = BuiltinDocs.bytes_center_doc) + final PyBytes bytes_center(int width, String fillchar) { + return (PyBytes)basebytes_center(width, fillchar); + } + + /** + * Implementation of Python count(sub). Return the number of non-overlapping + * occurrences of sub in this bytes. + * + * @param sub sequence to find (of a type viewable as a byte sequence) + * @return count of occurrences of sub within this bytes + */ + 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 bytes + */ + 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 bytes + */ + public int count(PyObject sub, PyObject start, PyObject end) { + return basebytes_count(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytes_count_doc) + final int bytes_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 bytes ends with the + * suffix. suffix can also be a tuple of suffixes to look for. + * + * @param suffix bytes to match, or object viewable as such, or a tuple of them + * @return true if and only if this bytes 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 bytes 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 bytes to be the slice [start:] of this + * bytes. + * + * @param suffix bytes to match, or object viewable as such, or a tuple of them + * @param start of slice in this bytes 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 bytes 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 bytes to be the slice + * [start:end] of this bytes. + * + * @param suffix bytes to match, or object viewable as such, or a tuple of them + * @param start of slice in this bytes to match + * @param end of slice in this bytes 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.bytes_endswith_doc) + final boolean bytes_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 bytes 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 bytes with tabs expanded + */ + public PyBytes expandtabs() { + return (PyBytes)basebytes_expandtabs(8); + } + + /** + * Implementation of Python expandtabs(tabsize): return a copy of the bytes + * 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 bytes with tabs expanded + */ + public PyBytes expandtabs(int tabsize) { + return (PyBytes)basebytes_expandtabs(tabsize); + } + + @ExposedMethod(defaults = "8", doc = BuiltinDocs.bytes_expandtabs_doc) + final PyBytes bytes_expandtabs(int tabsize) { + return (PyBytes)basebytes_expandtabs(tabsize); + } + + /** + * Implementation of Python find(sub). Return the lowest index in the bytes + * 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 bytes + */ + public int find(PyObject sub) { + return basebytes_find(sub, null, null); + } + + /** + * Implementation of Python find( sub [, start ] ). Return the lowest index in the + * bytes 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 bytes + */ + 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 bytes 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 bytes + */ + public int find(PyObject sub, PyObject start, PyObject end) { + return basebytes_find(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytes_find_doc) + final int bytes_find(PyObject sub, PyObject start, PyObject end) { + return basebytes_find(sub, start, end); + } + + /** + * Implementation of Python class method bytes.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: + * + *
+     * bytes.fromhex('B9 01EF') -> * bytes(b'\xb9\x01\xef')."
+     * 
+ * + * @param hex specification of the bytes + * @throws PyException (ValueError) if non-hex characters, or isolated ones, are encountered + */ + static PyBytes fromhex(String hex) throws PyException { + return bytes_fromhex(TYPE, hex); + } + + @ExposedClassMethod(doc = BuiltinDocs.bytes_fromhex_doc) + static PyBytes bytes_fromhex(PyType type, String hex) { + // I think type tells us the actual class but we always return exactly a bytes + // PyObject ba = type.__call__(); + PyBytes result = new PyBytes(); + basebytes_fromhex(result, hex); + return result; + } + + @ExposedMethod(doc = BuiltinDocs.bytes___getitem___doc) + final synchronized PyObject bytes___getitem__(PyObject index) { + // Let the SequenceIndexDelegate take care of it + return delegator.checkIdxAndGetItem(index); + } + + /** + * 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 bytes + */ + public int index(PyObject sub) { + return bytes_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 bytes + */ + public int index(PyObject sub, PyObject start) { + return bytes_index(sub, start, null); + } + + /** + * This type is not hashable. + * + * @throws PyException (TypeError) as this type is not hashable. + */ + @Override + public int hashCode() throws PyException { + return bytes___hash__(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes___hash___doc) + final int bytes___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 bytes + * @throws PyException ValueError if sub not found in bytes + */ + public int index(PyObject sub, PyObject start, PyObject end) throws PyException { + return bytes_index(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytes_index_doc) + final int bytes_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; + } + + // + // Character class operations + // + + @ExposedMethod(doc = BuiltinDocs.bytes_isalnum_doc) + final boolean bytes_isalnum() { + return basebytes_isalnum(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_isalpha_doc) + final boolean bytes_isalpha() { + return basebytes_isalpha(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_isdigit_doc) + final boolean bytes_isdigit() { + return basebytes_isdigit(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_islower_doc) + final boolean bytes_islower() { + return basebytes_islower(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_isspace_doc) + final boolean bytes_isspace() { + return basebytes_isspace(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_istitle_doc) + final boolean bytes_istitle() { + return basebytes_istitle(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_isupper_doc) + final boolean bytes_isupper() { + return basebytes_isupper(); + } + + // + // Case transformations + // + + @ExposedMethod(doc = BuiltinDocs.bytes_capitalize_doc) + final PyBytes bytes_capitalize() { + return (PyBytes)basebytes_capitalize(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_lower_doc) + final PyBytes bytes_lower() { + return (PyBytes)basebytes_lower(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_swapcase_doc) + final PyBytes bytes_swapcase() { + return (PyBytes)basebytes_swapcase(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_title_doc) + final PyBytes bytes_title() { + return (PyBytes)basebytes_title(); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_upper_doc) + final PyBytes bytes_upper() { + return (PyBytes)basebytes_upper(); + } + + /** + * Implementation of Python join(iterable). Return a bytes which + * is the concatenation of the bytess in the iterable iterable. The separator + * between elements is the bytes providing this method. + * + * @param iterable of bytes objects, or objects viewable as such. + * @return bytes produced by concatenation. + */ + public PyBytes join(PyObject iterable) { + return bytes_join(iterable); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_join_doc) + final PyBytes bytes_join(PyObject iterable) { + return basebytes_join(iterable.asIterable(), new Allocator() { + @Override + public PyBytes allocate(int size) { + return new PyBytes(size); + } + }); + } + + @ExposedMethod(doc = BuiltinDocs.bytes___len___doc) + final int bytes___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 bytes is + * returned if width is less than this.size(). + * + * @param width desired + * @return new bytes containing result + */ + public PyBytes ljust(int width) { + return (PyBytes)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 bytes 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 bytes containing the result + */ + public PyBytes ljust(int width, String fillchar) { + return (PyBytes)basebytes_ljust(width, fillchar); + } + + @ExposedMethod(defaults = "null", doc = BuiltinDocs.bytes_ljust_doc) + final PyBytes bytes_ljust(int width, String fillchar) { + // If this was immutable and width<=this.size we could return (this). + return (PyBytes)basebytes_ljust(width, fillchar); + } + + /** + * Implementation of Python lstrip(). Return a copy of the bytes with the + * leading whitespace characters removed. + * + * @return a bytes containing this value stripped of those bytes + */ + public PyBytes lstrip() { + return bytes_lstrip(null); + } + + /** + * Implementation of Python lstrip(bytes) + * + * Return a copy of the bytes 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 bytes containing this value stripped of those bytes (at the left) + */ + public PyBytes lstrip(PyObject bytes) { + return bytes_lstrip(bytes); + } + + @ExposedMethod(defaults = "null", doc = BuiltinDocs.bytes_lstrip_doc) + final synchronized PyBytes bytes_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.bytes_partition_doc) + final PyTuple bytes_partition(PyObject sep) { + return basebytes_partition(sep); + } + + @ExposedMethod(doc = BuiltinDocs.bytes___reduce___doc) + final PyObject bytes___reduce__() { + return basebytes___reduce__(); + } + + /** + * Implementation of Python rfind(sub). Return the highest index in the bytes + * 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 bytes + */ + public int rfind(PyObject sub) { + return basebytes_rfind(sub, null, null); + } + + /** + * Implementation of Python rfind( sub [, start ] ). Return the highest index in + * the bytes 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 bytes + */ + 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 bytes 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 bytes + */ + public int rfind(PyObject sub, PyObject start, PyObject end) { + return basebytes_rfind(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytes_rfind_doc) + final int bytes_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 bytes + */ + public int rindex(PyObject sub) { + return bytes_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 bytes + */ + public int rindex(PyObject sub, PyObject start) { + return bytes_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 bytes is + * returned if width is less than this.size(). + * + * @param width desired + * @return new bytes containing result + */ + public PyBytes rjust(int width) { + return (PyBytes)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 bytes 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 bytes containing the result + */ + public PyBytes rjust(int width, String fillchar) { + return (PyBytes)basebytes_rjust(width, fillchar); + } + + @ExposedMethod(defaults = "null", doc = BuiltinDocs.bytes_rjust_doc) + final PyBytes bytes_rjust(int width, String fillchar) { + return (PyBytes)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 bytes + */ + public int rindex(PyObject sub, PyObject start, PyObject end) { + return bytes_rindex(sub, start, end); + } + + @ExposedMethod(defaults = {"null", "null"}, doc = BuiltinDocs.bytes_rindex_doc) + final int bytes_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.bytes_rpartition_doc) + final PyTuple bytes_rpartition(PyObject sep) { + return basebytes_rpartition(sep); + } + + @ExposedMethod(defaults = {"null", "-1"}, doc = BuiltinDocs.bytes_rsplit_doc) + final PyList bytes_rsplit(PyObject sep, int maxsplit) { + return basebytes_rsplit(sep, maxsplit); + } + + /** + * Implementation of Python rstrip(). Return a copy of the bytes with the + * trailing whitespace characters removed. + * + * @return a bytes containing this value stripped of those bytes (at right) + */ + public PyBytes rstrip() { + return bytes_rstrip(null); + } + + /** + * Implementation of Python rstrip(bytes) + * + * Return a copy of the bytes 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 bytes containing this value stripped of those bytes (at right) + */ + public PyBytes rstrip(PyObject bytes) { + return bytes_rstrip(bytes); + } + + @ExposedMethod(defaults = "null", doc = BuiltinDocs.bytes_rstrip_doc) + final synchronized PyBytes bytes_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.bytes_split_doc) + final PyList bytes_split(PyObject sep, int maxsplit) { + return basebytes_split(sep, maxsplit); + } + + @ExposedMethod(defaults = "false", doc = BuiltinDocs.bytes_splitlines_doc) + final PyList bytes_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 bytes starts with the + * prefix. prefix can also be a tuple of prefixes to look for. + * + * @param prefix bytes to match, or object viewable as such, or a tuple of them + * @return true if and only if this bytes 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 bytes 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 bytes to be the slice [start:] of this + * bytes. + * + * @param prefix bytes to match, or object viewable as such, or a tuple of them + * @param start of slice in this bytes 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 bytes 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 bytes to be the slice + * [start:end] of this bytes. + * + * @param prefix bytes to match, or object viewable as such, or a tuple of them + * @param start of slice in this bytes to match + * @param end of slice in this bytes 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.bytes_startswith_doc) + final boolean bytes_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 bytes with the + * leading and trailing whitespace characters removed. + * + * @return a bytes containing this value stripped of those bytes (left and right) + */ + public PyBytes strip() { + return bytes_strip(null); + } + + /** + * Implementation of Python strip(bytes) + * + * Return a copy of the bytes with the leading and trailing characters removed. The bytes + * argument is anbytest 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 bytes containing this value stripped of those bytes (left and right) + */ + public PyBytes strip(PyObject bytes) { + return bytes_strip(bytes); + } + + @ExposedMethod(defaults = "null", doc = BuiltinDocs.bytes_strip_doc) + final synchronized PyBytes bytes_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); + } + + /** + * An overriding of the standard Java {@link #toString()} method, returning a printable + * expression of this bytes in the form bytes(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 bytes_repr(); + } + + @ExposedMethod(names = {"__repr__"}, doc = BuiltinDocs.bytes___repr___doc) + final synchronized String bytes_repr() { + return basebytes_repr("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 bytes. The + * built-in function str() is expected to call this method. + */ + @Override + public PyString __str__() { + return bytes_str(); + } + + @ExposedMethod(names = {"__str__"}, doc = BuiltinDocs.bytes___str___doc) + final PyString bytes_str() { + return new PyString(this.asString()); + } + + /** + * Implementation of Python translate(table). + * + * Return a copy of the bytes 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 bytes) + * @return translated bytes + */ + public PyBytes translate(PyObject table) { + return bytes_translate(table, null); + } + + /** + * Implementation of Python translate(table[, deletechars]). + * + * Return a copy of the bytes 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 bytes) + * @param deletechars object that may be regarded as a bytes, defining bytes to delete + * @return translated bytes + */ + public PyBytes translate(PyObject table, PyObject deletechars) { + return bytes_translate(table, deletechars); + } + + @ExposedMethod(defaults = "null", doc = BuiltinDocs.bytes_translate_doc) + final PyBytes bytes_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 bytes 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 so we may return + // this without modification, or a new + // PyBytes with the same contents. + if (this.getClass() == PyBytes.class) { + return this; + } + else { + return new PyBytes((BaseBytes)this); + } + } else { + int limit = offset + size; + // Loop over this bytes 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 new PyBytes((BaseBytes)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 bytes 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 bytes + */ + public PyBytes zfill(int width) { + return (PyBytes)basebytes_zfill(width); + } + + @ExposedMethod(doc = BuiltinDocs.bytes_zfill_doc) + final PyBytes bytes_zfill(int width) { + return (PyBytes)basebytes_zfill(width); + } +} diff --git a/src/org/python/core/PyBytesDerived.java b/src/org/python/core/PyBytesDerived.java new file mode 100644 index 000000000..60d035110 --- /dev/null +++ b/src/org/python/core/PyBytesDerived.java @@ -0,0 +1,1142 @@ +/* Generated file, do not modify. See jython/src/templates/gderived.py. */ +package org.python.core; + +import java.io.Serializable; +import org.python.core.finalization.FinalizeTrigger; +import org.python.core.finalization.FinalizablePyObjectDerived; + +public class PyBytesDerived extends PyBytes implements Slotted,FinalizablePyObjectDerived,TraverseprocDerived { + + public PyObject getSlot(int index) { + return slots[index]; + } + + public void setSlot(int index,PyObject value) { + slots[index]=value; + } + + private PyObject[]slots; + + public void __del_derived__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__del__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(); + } + } + + public void __ensure_finalizer__() { + FinalizeTrigger.ensureFinalizer(this); + } + + /* TraverseprocDerived implementation */ + public int traverseDerived(Visitproc visit,Object arg) { + int retVal; + for(int i=0;i super.__getitem__ -> __finditem__ -> __getitem__ + // + // By duplicating the (short) lookup and call code, we are safe, because + // the call chains will be: + // + // __finditem__ -> super.__finditem__ + // + // __getitem__ -> super.__getitem__ -> __finditem__ -> super.__finditem__ + + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getitem__"); + if (impl!=null) + return impl.__get__(this,self_type).__call__(key); + return super.__getitem__(key); + } + + public void __setitem__(PyObject key,PyObject value) { // ??? + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setitem__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(key,value); + return; + } + super.__setitem__(key,value); + } + + public PyObject __getslice__(PyObject start,PyObject stop,PyObject step) { // ??? + if (step!=null) { + return __getitem__(new PySlice(start,stop,step)); + } + PyType self_type=getType(); + PyObject impl=self_type.lookup("__getslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + return impl.__get__(this,self_type).__call__(indices[0],indices[1]); + } + return super.__getslice__(start,stop,step); + } + + public void __setslice__(PyObject start,PyObject stop,PyObject step,PyObject value) { + if (step!=null) { + __setitem__(new PySlice(start,stop,step),value); + return; + } + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + impl.__get__(this,self_type).__call__(indices[0],indices[1],value); + return; + } + super.__setslice__(start,stop,step,value); + } + + public void __delslice__(PyObject start,PyObject stop,PyObject step) { + if (step!=null) { + __delitem__(new PySlice(start,stop,step)); + return; + } + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delslice__"); + if (impl!=null) { + PyObject[]indices=PySlice.indices2(this,start,stop); + impl.__get__(this,self_type).__call__(indices[0],indices[1]); + return; + } + super.__delslice__(start,stop,step); + } + + public void __delitem__(PyObject key) { // ??? + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delitem__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(key); + return; + } + super.__delitem__(key); + } + + public PyObject __call__(PyObject args[],String keywords[]) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__call__"); + if (impl!=null) { + return impl.__get__(this,self_type).__call__(args,keywords); + } + return super.__call__(args,keywords); + } + + public PyObject __findattr_ex__(String name) { + return Deriveds.__findattr_ex__(this,name); + } + + public void __setattr__(String name,PyObject value) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__setattr__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(PyString.fromInterned(name),value); + //CPython does not support instance-acquired finalizers. + //So we don't check for __del__ here. + return; + } + super.__setattr__(name,value); + } + + public void __delattr__(String name) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delattr__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(PyString.fromInterned(name)); + return; + } + super.__delattr__(name); + } + + public PyObject __get__(PyObject obj,PyObject type) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__get__"); + if (impl!=null) { + if (obj==null) + obj=Py.None; + if (type==null) + type=Py.None; + return impl.__get__(this,self_type).__call__(obj,type); + } + return super.__get__(obj,type); + } + + public void __set__(PyObject obj,PyObject value) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__set__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(obj,value); + return; + } + super.__set__(obj,value); + } + + public void __delete__(PyObject obj) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__delete__"); + if (impl!=null) { + impl.__get__(this,self_type).__call__(obj); + return; + } + super.__delete__(obj); + } + + public PyObject __pow__(PyObject other,PyObject modulo) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__pow__"); + if (impl!=null) { + PyObject res; + if (modulo==null) { + res=impl.__get__(this,self_type).__call__(other); + } else { + res=impl.__get__(this,self_type).__call__(other,modulo); + } + if (res==Py.NotImplemented) + return null; + return res; + } + return super.__pow__(other,modulo); + } + + public void dispatch__init__(PyObject[]args,String[]keywords) { + Deriveds.dispatch__init__(this,args,keywords); + } + + public PyObject __index__() { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__index__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(); + if (res instanceof PyInteger||res instanceof PyLong) { + return res; + } + throw Py.TypeError(String.format("__index__ returned non-(int,long) (type %s)",res.getType().fastGetName())); + } + return super.__index__(); + } + + public Object __tojava__(Class c) { + // If we are not being asked by the "default" conversion to java, then + // we can provide this as the result, as long as it is a instance of the + // specified class. Without this, derived.__tojava__(PyObject.class) + // would broke. (And that's not pure speculation: PyReflectedFunction's + // ReflectedArgs asks for things like that). + if ((c!=Object.class)&&(c!=Serializable.class)&&(c.isInstance(this))) { + return this; + } + // Otherwise, we call the derived __tojava__, if it exists: + PyType self_type=getType(); + PyObject impl=self_type.lookup("__tojava__"); + if (impl!=null) { + PyObject delegate=impl.__get__(this,self_type).__call__(Py.java2py(c)); + if (delegate!=this) + return delegate.__tojava__(Object.class); + } + return super.__tojava__(c); + } + + public Object __coerce_ex__(PyObject o) { + PyType self_type=getType(); + PyObject impl=self_type.lookup("__coerce__"); + if (impl!=null) { + PyObject res=impl.__get__(this,self_type).__call__(o); + if (res==Py.NotImplemented) + return Py.None; + if (!(res instanceof PyTuple)) + throw Py.TypeError("__coerce__ didn't return a 2-tuple"); + return((PyTuple)res).getArray(); + } + return super.__coerce_ex__(o); + } + +} diff --git a/src/org/python/core/PyRange.java b/src/org/python/core/PyRange.java index 47049fb82..31b9b6e7f 100644 --- a/src/org/python/core/PyRange.java +++ b/src/org/python/core/PyRange.java @@ -136,12 +136,12 @@ public PyObject range___reversed__() { return range_reverse(); } - private final PyXRangeIter range_iter() { - return new PyXRangeIter(0, (long)start, (long)step, (long)len); + private final PyRangeIter range_iter() { + return new PyRangeIter(0, (long)start, (long)step, (long)len); } - private final PyXRangeIter range_reverse() { - return new PyXRangeIter(0, + private final PyRangeIter range_reverse() { + return new PyRangeIter(0, (start + (len - 1) * step), // start (0 - step), // step (negative value) len); diff --git a/src/org/python/core/PyXRangeIter.java b/src/org/python/core/PyRangeIter.java similarity index 82% rename from src/org/python/core/PyXRangeIter.java rename to src/org/python/core/PyRangeIter.java index 1c34bb23d..0a64155f6 100644 --- a/src/org/python/core/PyXRangeIter.java +++ b/src/org/python/core/PyRangeIter.java @@ -8,9 +8,9 @@ * Specially optimized xrange iterator. */ @ExposedType(name = "rangeiterator", base = PyObject.class, isBaseType = false) -public class PyXRangeIter extends PyIterator { +public class PyRangeIter extends PyIterator { - public static final PyType TYPE = PyType.fromClass(PyXRangeIter.class); + public static final PyType TYPE = PyType.fromClass(PyRangeIter.class); static { TYPE.setName("rangeiterator"); } @@ -20,7 +20,7 @@ public class PyXRangeIter extends PyIterator { private long step; private long len; - public PyXRangeIter(long index, long start, long step, long len) { + public PyRangeIter(long index, long start, long step, long len) { super(TYPE); this.index = index; this.start = start; diff --git a/src/org/python/core/PyUnicode.java b/src/org/python/core/PyUnicode.java index 6ce61a2ef..f347b5639 100644 --- a/src/org/python/core/PyUnicode.java +++ b/src/org/python/core/PyUnicode.java @@ -24,7 +24,6 @@ @Untraversable @ExposedType(name = "unicode", base = PyBaseString.class, doc = BuiltinDocs.str_doc) public class PyUnicode extends PyString implements Iterable { - /** * Nearly every significant method comes in two versions: one applicable when the string * contains only basic plane characters, and one that is correct when supplementary characters @@ -684,7 +683,7 @@ public PyString __repr__() { @ExposedMethod(doc = BuiltinDocs.str___repr___doc) final PyString unicode___repr__() { - return new PyString("u" + encode_UnicodeEscape(getString(), true)); + return new PyString(encode_UnicodeEscape(getString(), true)); } @ExposedMethod(doc = BuiltinDocs.str___getitem___doc) diff --git a/src/org/python/core/__builtin__.java b/src/org/python/core/__builtin__.java index 7318a1658..e692d38cf 100644 --- a/src/org/python/core/__builtin__.java +++ b/src/org/python/core/__builtin__.java @@ -279,6 +279,8 @@ public static void fillWithBuiltins(PyObject dict) { dict.__setitem__("classmethod", PyClassMethod.TYPE); dict.__setitem__("super", PySuper.TYPE); dict.__setitem__("str", PyString.TYPE); + + // TODO: remove basestring, unicode... dict.__setitem__("unicode", PyUnicode.TYPE); dict.__setitem__("basestring", PyBaseString.TYPE); dict.__setitem__("file", PyFile.TYPE); @@ -290,7 +292,7 @@ public static void fillWithBuiltins(PyObject dict) { dict.__setitem__("Ellipsis", Py.Ellipsis); dict.__setitem__("True", Py.True); dict.__setitem__("False", Py.False); - dict.__setitem__("bytes", PyString.TYPE); + dict.__setitem__("bytes", PyBytes.TYPE); dict.__setitem__("bytearray", PyByteArray.TYPE); dict.__setitem__("buffer", Py2kBuffer.TYPE); dict.__setitem__("memoryview", PyMemoryView.TYPE); diff --git a/src/org/python/core/util/Allocator.java b/src/org/python/core/util/Allocator.java new file mode 100644 index 000000000..c9abd87da --- /dev/null +++ b/src/org/python/core/util/Allocator.java @@ -0,0 +1,5 @@ +package org.python.core.util; + +public interface Allocator { + public T allocate(int size); +} diff --git a/src/templates/bytes.derived b/src/templates/bytes.derived new file mode 100644 index 000000000..66903bb60 --- /dev/null +++ b/src/templates/bytes.derived @@ -0,0 +1,5 @@ +base_class: PyBytes +want_dict: true +ctr: PyBytes b +incl: object +no_toString: true diff --git a/src/templates/mappings b/src/templates/mappings index 85d9731a6..3f403736b 100644 --- a/src/templates/mappings +++ b/src/templates/mappings @@ -14,6 +14,7 @@ PyBZ2Decompressor.derived:org.python.modules.bz2.PyBZ2DecompressorDerived PyStruct.derived:org.python.modules.PyStructDerived array.derived:org.python.core.PyArrayDerived bytearray.derived:org.python.core.PyByteArrayDerived +bytes.derived:org.python.core.PyBytesDerived classmethod.derived:org.python.core.PyClassMethodDerived chain.derived:org.python.modules.itertools.chainDerived combinationsWithReplacement.derived:org.python.modules.itertools.combinationsWithReplacementDerived