diff -r e0f84f668a19 -r b0cf73711ebd Lib/test/test_os_jy.py --- a/Lib/test/test_os_jy.py Thu Mar 19 17:06:53 2015 -0700 +++ b/Lib/test/test_os_jy.py Fri Mar 20 20:00:58 2015 -0700 @@ -4,13 +4,15 @@ Made for Jython. """ +import os +import sys +import glob import array import errno -import glob -import os +import struct +import unittest import subprocess -import sys -import unittest + from test import test_support from java.io import File @@ -74,6 +76,54 @@ content = f.read() self.assertEqual(content, 2 * b'onetwothree') + def test_issue1011(self): + # prepare the input file containing 256 bytes of sorted byte-sized numbers + fd = file(test_support.TESTFN, 'wb') + try: + for x in range(256): + fd.write(chr(x)) + finally: + fd.close() + + # reopen in read/append mode + fd = file(test_support.TESTFN, 'rb+') + try: + # read forward from the beginning + for x in range(256): + pos = fd.tell() + self.assertEqual(pos, x, + '[forward] before read: pos should be %d but is %d' % (x, pos)) + + # read just one byte + c = struct.unpack('B', fd.read(1))[0] + + pos = fd.tell() + self.assertEqual(pos, x + 1, + '[forward] after read: pos should be %d but is %d' % (x + 1, pos)) + + self.assertEqual(c, x) + + # read backward from the end + fd.seek(-1, os.SEEK_END) + for x in range(255, -1, -1): + pos = fd.tell() + self.assertEqual(pos, x, + '[backward] before read: pos should be %d but is %d' % (x, pos)) + + # read just one byte + c = ord(fd.read(1)) + + pos = fd.tell() + self.assertEqual(pos, x + 1, + '[backward] after read: pos should be %d but is %d' % (x + 1, pos)) + + self.assertEqual(c, x) + + if x > 0: + fd.seek(-2, os.SEEK_CUR) + finally: + fd.close() + class OSDirTestCase(unittest.TestCase): diff -r e0f84f668a19 -r b0cf73711ebd src/org/python/core/io/BufferedIOBase.java --- a/src/org/python/core/io/BufferedIOBase.java Thu Mar 19 17:06:53 2015 -0700 +++ b/src/org/python/core/io/BufferedIOBase.java Fri Mar 20 20:00:58 2015 -0700 @@ -26,7 +26,7 @@ } ByteBuffer bytes = ByteBuffer.allocate(size); - readinto(bytes); + readinto(bytes, false); // flip()ing here is more convenient as there's no real use // case for appending to buffers returned from read. readinto // doesn't/shouldn't flip() @@ -58,6 +58,21 @@ } /** + * Read up to bytes.remaining() bytes into the given ByteBuffer, but control + * whether to attempt to buffer the rest of the underlying stream. + * + * Returns the number of bytes read (0 for EOF) + * + * @param bytes a ByteBuffer to read bytes into + * @param buffered whether to buffer the remaining bytes of the stream, if we can + * @return the amount of data read as an int + */ + protected int readinto(ByteBuffer bytes, boolean buffered) { + unsupported("readinto"); + return -1; + } + + /** * Write the given ByteBuffer to the IO stream. * * Returns the number of bytes written, which may be less than diff -r e0f84f668a19 -r b0cf73711ebd src/org/python/core/io/BufferedReader.java --- a/src/org/python/core/io/BufferedReader.java Thu Mar 19 17:06:53 2015 -0700 +++ b/src/org/python/core/io/BufferedReader.java Fri Mar 20 20:00:58 2015 -0700 @@ -29,32 +29,43 @@ @Override public int readinto(ByteBuffer bytes) { + return readinto(bytes, true); + } + + @Override + protected int readinto(ByteBuffer bytes, boolean buffered) { int size = bytes.remaining(); if (size == 0) { return 0; } - if (buffer.remaining() >= size) { - // Fulfill the read entirely from the buffer - int bufferLimit = buffer.limit(); - buffer.limit(buffer.position() + size); + long read; + + if (buffered) { + if (buffer.remaining() >= size) { + // Fulfill the read entirely from the buffer + int bufferLimit = buffer.limit(); + buffer.limit(buffer.position() + size); + bytes.put(buffer); + buffer.limit(bufferLimit); + return size; + } + + // Drain the buffer then request more from the RawIO bytes.put(buffer); - buffer.limit(bufferLimit); - return size; + buffer.clear(); + + // Only attempt one read. The buffering layer should not wait + // for more data (block) to fulfill the entire read + read = rawIO.readinto(new ByteBuffer[]{bytes, buffer}); + read -= buffer.flip().limit(); + } else { + read = rawIO.readinto(bytes); } - // Drain the buffer then request more from the RawIO - bytes.put(buffer); - buffer.clear(); - - // Only attempt one read. The buffering layer should not wait - // for more data (block) to fulfill the entire read - long read = rawIO.readinto(new ByteBuffer[] {bytes, buffer}); - read -= buffer.flip().limit(); - // This is an int after subtracting the buffer size anyway - return (int)read; + return (int) read; } @Override diff -r e0f84f668a19 -r b0cf73711ebd src/org/python/core/io/FileIO.java --- a/src/org/python/core/io/FileIO.java Thu Mar 19 17:06:53 2015 -0700 +++ b/src/org/python/core/io/FileIO.java Fri Mar 20 20:00:58 2015 -0700 @@ -30,6 +30,14 @@ */ public class FileIO extends RawIOBase { + // would be nicer if we directly imported from os, but crazy to do so + // since in python code itself + private static class os { + public static final int SEEK_SET = 0; + public static final int SEEK_CUR = 1; + public static final int SEEK_END = 2; + } + /** The underlying file channel */ private FileChannel fileChannel; @@ -204,7 +212,7 @@ */ private void initPosition() { if (appending) { - seek(0, 2); + seek(0, os.SEEK_END); } else if (writing && !reading) { try { fileChannel.truncate(0); @@ -367,12 +375,12 @@ checkClosed(); try { switch (whence) { - case 0: + case os.SEEK_SET: break; - case 1: + case os.SEEK_CUR: pos += fileChannel.position(); break; - case 2: + case os.SEEK_END: pos += fileChannel.size(); break; default: