Package warrenfalk.lzma

Source Code of warrenfalk.lzma.LzmaInputStream$Marshaler

/*
* Copyright 2011 Warren Falk
* This program is distributed under the terms of the GNU General Public License (LGPL)
*
* This file is part of lzma-java
*
* lzma-java is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* See https://bitbucket.org/warren/lzma-java for the latest version
*/
package warrenfalk.lzma;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;

import warrenfalk.util.BinCoder;

import SevenZip.Compression.LZMA.Decoder;

/**
* This particular implementation exposes an input stream which, when read from, gets
* its data from an output stream from an Lzma SDK decoder on another thread.
*
* The drawback of this is that exceptions on the lzma decoding (input stream) side of
* the operation are not thrown in the thread that's supplying them with data as is usually
* the case, so these will be marshaled to the calling thread and rethrown at the next
* available opportunity (i.e. next client call).
*
* @author warren
*
*/
public class LzmaInputStream extends InputStream   implements
    ReadableByteChannel {
  public final Marshaler marshaler;
  public final DecodeThread thread;
 
  /**
   * Creates an LzmaInputStream with default settings
   * @param stream
   * @throws IOException
   */
  public LzmaInputStream(InputStream stream) throws IOException {
    this(stream, 4096);
  }
 
  public LzmaInputStream(InputStream stream, int marshalBufferSize) throws IOException {
    this(stream, new byte[marshalBufferSize]);
  }
 
  public LzmaInputStream(InputStream stream, byte[] marshalBuffer) throws IOException {
    marshaler = new Marshaler(marshalBuffer);
    thread = new DecodeThread(stream);
    thread.start();
  }
 
  public class DecodeThread extends Thread {
    final Decoder decoder;
    final InputStream stream;
    final long outSize;
   
    public DecodeThread(InputStream stream) throws IOException {
      this.decoder = new Decoder();
      byte[] properties = new byte[5];
      if (stream.read(properties, 0, properties.length) != properties.length)
        throw new IOException("unexpected end of stream");
      this.decoder.SetDecoderProperties(properties);
      long outSize = BinCoder.getInt8(stream);
      this.outSize = outSize;
      this.stream = stream;
    }
   
    @Override
    public void run() {
      try {
        decoder.Code(stream, marshaler, outSize);
        marshaler.end();
      }
      catch (Throwable t) {
        marshaler._throw(t);
      }
    }
  }
 
  public class Marshaler extends OutputStream {
    final byte[] buffer;
    int wcursor;
    int rcursor;
    int size;
    boolean eos;
    Throwable t;

    public Marshaler(byte[] marshalBuffer) {
      this.buffer = marshalBuffer;
    }

    public int read(byte[] b, int off, int len) throws IOException {
      try {
        synchronized (buffer) {
          while (size == 0) {
            if (eos)
              return -1;
            buffer.wait();
          }
          boolean wasFull = buffer.length == size;
          if (len > size)
            len = size;
          // a copy operation can be split into two parts if the data to be read is wrapped to the beginning of the buffer
          int len1 = len;
          int end = rcursor + len;
          int len2 = end - buffer.length;
          if (len2 > 0) {
            len1 -= len2;
            System.arraycopy(buffer, 0, b, off + len1, len2);
          }
          if (len1 > 0) {
            System.arraycopy(buffer, rcursor, b, off, len1);
          }
          rcursor = (rcursor + len) % buffer.length;
          size -= len;
          if (wasFull)
            buffer.notify();
          rethrow();
        }
        return len;
      }
      catch (InterruptedException ie) {
        throw new IOException(ie);
      }
    }
   
    public int read(ByteBuffer b) throws IOException {
      try {
        int len;
        synchronized (buffer) {
          while (size == 0) {
            if (eos)
              return -1;
            buffer.wait();
          }
          boolean wasFull = buffer.length == size;
          len = b.remaining();
          if (len > size)
            len = size;
          // a copy operation can be split into two parts if the data to be read is wrapped to the beginning of the buffer
          int len1 = len;
          int end = rcursor + len;
          int len2 = end - buffer.length;
          if (len2 > 0)
            len1 -= len2;
          if (len1 > 0)
            b.put(buffer, rcursor, len1);
          if (len2 > 0)
            b.put(buffer, 0, len2);
          rcursor = (rcursor + len) % buffer.length;
          size -= len;
          if (wasFull)
            buffer.notify();
          rethrow();
        }
        return len;
      }
      catch (InterruptedException ie) {
        throw new IOException(ie);
      }
    }
   
    public int read() throws IOException {
      try {
        int b;
        synchronized (buffer) {
          while (size == 0) {
            if (eos)
              return -1;
            buffer.wait();
          }
          boolean wasFull = buffer.length == size;
          // a copy operation can be split into two parts if the data to be read is wrapped to the beginning of the buffer
          b = buffer[rcursor++];
          rcursor %= buffer.length;
          size--;
          if (wasFull)
            buffer.notify();
          rethrow();
        }
        return b;
      }
      catch (InterruptedException ie) {
        throw new IOException(ie);
      }
    }
   
    @Override
    public void write(byte[] b, int off, final int len) throws IOException {
      try {
        int remain = len;
        while (remain > 0) {
          synchronized (buffer) {
            if (eos)
              throw new IOException("Cannot write to a stream after it has been closed");
            while (buffer.length == size)
              buffer.wait();
            boolean wasEmpty = 0 == size;
            int avail = buffer.length - size;
            int write = remain;
            if (write > avail)
              write = avail;
            if (write > 0) {
              // a copy operation can be split into two parts if the data to be written is wrapped to the beginning of the buffer
              int len1 = write;
              int end = wcursor + write;
              int len2 = end - buffer.length;
              if (len2 > 0) {
                len1 -= len2;
                System.arraycopy(b, off + len1, buffer, 0, len2);
              }
              if (len1 > 0) {
                System.arraycopy(b, off, buffer, wcursor, len1);
              }
              size += write;
              wcursor = (wcursor + write) % buffer.length;
              if (wasEmpty)
                buffer.notify();
              remain -= write;
              off += write;
            }
          }
        }
      }
      catch (InterruptedException ie) {
        throw new IOException(ie);
      }
    }
   
    @Override
    public void write(int b) throws IOException {
      try {
        synchronized (buffer) {
          if (eos)
            throw new IOException("Cannot write to a stream after it has been closed");
          while (buffer.length == size)
            buffer.wait();
          boolean wasEmpty = 0 == size;
          buffer[wcursor++] = (byte)b;
          if (wcursor == buffer.length)
            wcursor = 0;
          size++;
          if (wasEmpty)
            buffer.notify();
        }
      }
      catch (InterruptedException ie) {
        throw new IOException(ie);
      }
    }

    public void end() throws IOException {
      synchronized (buffer) {
        eos = true;
        buffer.notify();
      }
    }
   
    void _throw(Throwable t) {
      this.t = t;
    }

    public void rethrow() throws IOException {
      if (this.t == null)
        return;
      if (this.t instanceof IOException)
        throw (IOException)this.t;
      if (this.t instanceof RuntimeException)
        throw (RuntimeException)this.t;
      if (this.t instanceof Error)
        throw (Error)this.t;
      throw new IOException(t);
    }
  }

  @Override
  public void close() throws IOException {
    marshaler.end();
    try {
      thread.join();
    }
    catch (InterruptedException ie) {
      throw new IOException(ie);
    }
    marshaler.rethrow();
    super.close();
  }


  @Override
  public boolean isOpen() {
    // TODO: need to think about whether this is correct or not
    return true;
  }

  @Override
  public int read(ByteBuffer b) throws IOException {
    return marshaler.read(b);
  }

  @Override
  public int read() throws IOException {
    return marshaler.read();
  }
 
  @Override
  public int read(byte[] b, int off, int len) throws IOException {
    return marshaler.read(b, off, len);
  }

}
TOP

Related Classes of warrenfalk.lzma.LzmaInputStream$Marshaler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
script>