0

Suppose i have a input stream and want to reverse it !

I similar question How to get the content of an input stream in reverse order? but firstly thats about 8 years old and also its not what i want exactly!

Suppose i have a InputStream like :

FileInputStream in = new FileInputStream(new File("some_random_file.bin"));

Note this is not specifically for Text files but for binaries!

Now,

I have a way of reversing it :

public static InputStream reverseStream(InputStream in) throws Exception{
    byte bytes[] = in.readAllBytes();
    byte bytesRev[] = new byte[bytes.length];
    for(int i=bytes.length - 1, j = 0;i >= 0; i--, j++)
        bytesRev[i] = bytes[j];
    return new ByteArrayInputStream(bytesRev);
}

But i am not sure this is the most efficient way to do it!

I want to have a efficient way to achieve this even for Large Files!

Jaysmito Mukherjee
  • 1,467
  • 2
  • 10
  • 29
  • 4
    Streams, like their bigger cousins rivers, flow only in one direction. Only with a big enough bucket can you reverse them (once they've fully flown). – Kayaman Apr 28 '21 at 19:09
  • @Kayaman i dont know what size bucket to keep! A huge bucket is not good for a cup water whereas a small one wont work for larger scales! I am looking for a efficient bucket for both tasks together – Jaysmito Mukherjee Apr 28 '21 at 19:12
  • You can't *truly* reverse streams is the point I was trying to make. So you can't read a large file in reverse, although you could create a (clumsy) `ReverseFileInputStream`, but that would be silly. Files are meant to be read in one direction, beginning to end (although you can [seek](https://docs.oracle.com/javase/8/docs/api/java/io/RandomAccessFile.html) inside a file, but that's no longer streaming). – Kayaman Apr 28 '21 at 19:13
  • An example of a stream, is a [livestream](https://en.wikipedia.org/wiki/Livestreaming). You can't reverse that, because the end simply didn't happen yet. – Ivar Apr 28 '21 at 19:28
  • 4
    *Universally and usefully* reversing *any given `InputStream`* is effectively impossible (as Kayaman so poetically proved). But a *specific* solution to reversing the flow of a specific stream is quite possible in some cases. So could you tell us what the underlying goal is? This might just be a [XY Problem](https://xyproblem.info). – Joachim Sauer Apr 28 '21 at 19:41
  • 1
    Large earthquakes can reverse a river. Maybe flipping the HDD over violently could reverse the stream. – SephB Apr 28 '21 at 20:13
  • 1
    @Kayaman - Why is reading a file in reverse "silly"? – Ted Hopp Apr 28 '21 at 21:36
  • @TedHopp if we consider from the POV of spinning disks, if you're constantly trying to *stream* a file in reverse, maybe it was written the wrong way around. SSDs of course alleviate that problem and there are plenty of `ReverseFileInputStream` implementations available. But you'd need to get something out of it as well, streaming a raw audio file backwards might be semi-useful, but for a general file format? Just use `RandomAccessFile` directly. Reading a file backwards isn't silly (large log files for example), streaming a file backwards is a bit. – Kayaman Apr 29 '21 at 04:56
  • See also https://stackoverflow.com/questions/6011345/read-a-file-line-by-line-in-reverse-order – Kayaman Apr 29 '21 at 07:44

1 Answers1

2

If you're willing to read the entire file into memory, then your solution is pretty good. The memory footprint can be improved by reversing the contents in placed rather than allocating a second array to store the reversed contents:

public static InputStream reverseStream(InputStream in) throws Exception{
    byte bytes[] = in.readAllBytes();
    for(int i=bytes.length - 1, j = 0;i >j; i--, j++) {
        byte tmp = bytes[i];
        bytes[i] = bytes[j];
        bytes[j] = tmp;
    }
    return new ByteArrayInputStream(bytes);
}

If the file is so large that you don't want to load it all at once, then you will need to use class java.io.RandomAccessFile to read the file in reverse order. You will need to use some sort of internal buffering to avoid horrible performance. You can wrap this up in your own implementation of InputStream that reads backwards through the buffer, loading a new buffer-full on the fly as necessary.

Here's my stab at a class that does this. This code is completely untested (although it compiles).

/**
 * An input stream that reads a file in reverse. (UNTESTED)
 *
 * @author Ted Hopp
 */
class ReverseFileInputStream extends InputStream {
    private final RandomAccessFile file;
    /** Internal buffer for reading chunks of the file in reverse. */
    private final byte[] buffer;
    /** Position of the start of the buffer in the file. */
    private long bufferPosition;
    /** Position of the next byte to be read from the buffer. */
    private int bufferCursor;

    public ReverseFileInputStream(File file, int bufferSize) throws IOException {
        this.file = new RandomAccessFile(file, "r");
        buffer = new byte[bufferSize];
        bufferPosition = this.file.length();
        bufferCursor = -1;
    }

    @Override public int read() throws IOException {
        if (bufferCursor < 0) {
            fillBuffer();
        }
        return bufferCursor < 0 ? -1 : (buffer[bufferCursor--] & 0xff);
    }

    @Override public void close() throws IOException {
        file.close();
    }

    private void fillBuffer() throws IOException {
        if (bufferPosition > 0) {
            long newBufferPosition = Math.max(0L, bufferPosition - buffer.length);
            bufferCursor = (int) (bufferPosition - newBufferPosition);
            file.seek(newBufferPosition);
            file.readFully(buffer, 0, bufferCursor--);
            bufferPosition = newBufferPosition;
        }
    }
}

Note that if you try to wrap a Reader around this, the result will likely be nonsense unless the text encoding of the underlying file is one byte per character. Likewise with DataInputStream, etc.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521