All Downloads are FREE. Search and download functionalities are using the official Maven repository.

darwin.jopenctm.io.CtmOutputStream Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 Daniel Heinrich
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * (version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see 
 * or write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301  USA.
 */
package darwin.jopenctm.io;

import java.io.*;

import lzma.sdk.lzma.Encoder;
import lzma.streams.LzmaEncoderWrapper;

/**
 *
 * @author daniel
 */
public class CtmOutputStream extends DataOutputStream {

    private final int compressionLevel;

    public CtmOutputStream(OutputStream out) {
        this(5, out);
    }

    public CtmOutputStream(int compressionLevel, OutputStream out) {
        super(out);
        this.compressionLevel = compressionLevel;
    }

    public void writeString(String text) throws IOException {
        if (text != null) {
            writeLittleInt(text.length());
            write(text.getBytes());
        } else {
            writeLittleInt(0);
        }
    }

    public void writeLittleInt(int v) throws IOException {
        out.write(v & 0xFF);
        out.write((v >>> 8) & 0xFF);
        out.write((v >>> 16) & 0xFF);
        out.write((v >>> 24) & 0xFF);
    }

    public void writeLittleIntArray(int[] v) throws IOException {
        for (int a : v) {
            writeLittleInt(a);
        }
    }

    public void writeLittleFloat(float v) throws IOException {
        writeLittleInt(Float.floatToIntBits(v));
    }

    public void writeLittleFloatArray(float[] v) throws IOException {
        for (float a : v) {
            writeLittleFloat(a);
        }
    }

    public void writePackedInts(int[] data, int count, int size, boolean signed) throws IOException {
        assert data.length >= count * size : "The data to be written is smaller"
                + " as stated by other parameters. Needed: " + (count * size) + " Provided: " + data.length;
        // Allocate memory for interleaved array
        byte[] tmp = new byte[count * size * 4];

        // Convert integers to an interleaved array
        for (int i = 0; i < count; ++i) {
            for (int k = 0; k < size; ++k) {
                int value = data[i * size + k];
                // Convert two's complement to signed magnitude?
                if (signed) {
                    value = value < 0 ? -1 - (value << 1) : value << 1;
                }
                interleavedInsert(value, tmp, i + k * count, count * size);
            }
        }

        writeCompressedData(tmp);
    }

    public void writePackedFloats(float[] data, int count, int size) throws IOException {
        assert data.length >= count * size : "The data to be written is smaller"
                + " as stated by other parameters. Needed: " + (count * size) + " Provided: " + data.length;
        // Allocate memory for interleaved array
        byte[] tmp = new byte[count * size * 4];

        // Convert floats to an interleaved array
        for (int x = 0; x < count; ++x) {
            for (int y = 0; y < size; ++y) {
                int value = Float.floatToIntBits(data[x * size + y]);
                interleavedInsert(value, tmp, x + y * count, count * size);
            }
        }
        writeCompressedData(tmp);
    }

    public static void interleavedInsert(int value, byte[] data, int offset, int stride) {
        data[offset + 3 * stride] = (byte) (value & 0xff);
        data[offset + 2 * stride] = (byte) ((value >> 8) & 0xff);
        data[offset + stride] = (byte) ((value >> 16) & 0xff);
        data[offset] = (byte) ((value >> 24) & 0xff);
    }

    public void writeCompressedData(byte[] data) throws IOException {
        //some magic size as in the OpenCTM reference implementation
        ByteArrayOutputStream bout = new ByteArrayOutputStream(1000 + data.length);

        Encoder enc = new Encoder();
        enc.setEndMarkerMode(false);
        if (compressionLevel <= 5) {
            enc.setDictionarySize(1 << (compressionLevel * 2 + 14));
        } else if (compressionLevel == 6) {
            enc.setDictionarySize(1 << 25);
        } else {
            enc.setDictionarySize(1 << 26);
        }
        enc.setNumFastBytes(compressionLevel < 7 ? 32 : 64);
        
        enc.code(new ByteArrayInputStream(data), bout, data.length, -1, null);
//        try (OutputStream lzout = new LzmaOutputStream(bout, new CustomWrapper(enc))) {
//            lzout.write(data);
//        }

        //This is the custom way of OpenCTM to write the LZMA properties
        this.writeLittleInt(bout.size());
        enc.writeCoderProperties(this);
        bout.writeTo(this);
//        write(data);
    }

    private static class CustomWrapper extends LzmaEncoderWrapper {

        private final Encoder e;

        CustomWrapper(Encoder encoder) {
            super(encoder);
            e = encoder;
        }

        @Override
        public void code(InputStream in, OutputStream out) throws IOException {
            //both int attributes aren't used inside the method
            e.code(in, out, -1, -1, null);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy