SnappyCodec.java

/*--------------------------------------------------------------------------
 *  Copyright 2011 Taro L. Saito
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *--------------------------------------------------------------------------*/
//--------------------------------------
// XerialJ
//
// SnappyCodec.java
// Since: 2011/04/03 14:50:20
//
// $URL$
// $Author$
//--------------------------------------
package org.xerial.snappy;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

/**
 * Preamble header for {@link SnappyOutputStream}.
 * <p/>
 * <p>
 * The magic header is the following 8 bytes data:
 * <p/>
 * <pre>
 * -126, 'S', 'N', 'A', 'P', 'P', 'Y', 0
 * </pre>
 * <p/>
 * </p>
 *
 * @author leo
 */
public class SnappyCodec
{
    static final byte[] MAGIC_HEADER = new byte[] {-126, 'S', 'N', 'A', 'P', 'P', 'Y', 0};
    public static final int MAGIC_LEN = MAGIC_HEADER.length;
    public static final int HEADER_SIZE = MAGIC_LEN + 8;
    public static final int MAGIC_HEADER_HEAD = SnappyOutputStream.readInt(MAGIC_HEADER, 0);

    static {
        assert (MAGIC_HEADER_HEAD < 0);
    }

    public static final int DEFAULT_VERSION = 1;
    public static final int MINIMUM_COMPATIBLE_VERSION = 1;
    public static final SnappyCodec currentHeader = new SnappyCodec(MAGIC_HEADER, DEFAULT_VERSION, MINIMUM_COMPATIBLE_VERSION);

    public final byte[] magic;
    public final int version;
    public final int compatibleVersion;
    private final byte[] headerArray;

    private SnappyCodec(byte[] magic, int version, int compatibleVersion)
    {
        this.magic = magic;
        this.version = version;
        this.compatibleVersion = compatibleVersion;

        ByteArrayOutputStream header = new ByteArrayOutputStream(HEADER_SIZE);
        DataOutputStream d = new DataOutputStream(header);
        try {
            d.write(magic, 0, MAGIC_LEN);
            d.writeInt(version);
            d.writeInt(compatibleVersion);
            d.close();
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
        headerArray = header.toByteArray();
    }

    public static byte[] getMagicHeader()
    {
        return MAGIC_HEADER.clone();
    }

    @Override
    public String toString()
    {
        return String.format("version:%d, compatible version:%d", version, compatibleVersion);
    }

    public static int headerSize()
    {
        return HEADER_SIZE;
    }

    public int writeHeader(byte[] dst, int dstOffset)
    {
        System.arraycopy(headerArray, 0, dst, dstOffset, headerArray.length);
        return headerArray.length;
    }

    public int writeHeader(OutputStream out)
            throws IOException
    {
        out.write(headerArray, 0, headerArray.length);
        return headerArray.length;
    }

    public boolean isValidMagicHeader()
    {
        return Arrays.equals(MAGIC_HEADER, magic);
    }

    public static boolean hasMagicHeaderPrefix(byte[] b) {
        int limit = Math.min(MAGIC_LEN, b.length);
        int i = 0;
        while(i < limit) {
            if(b[i] != MAGIC_HEADER[i]) {
                return false;
            }
            ++i;
        }
        return true;
    }

    public static SnappyCodec readHeader(InputStream in)
            throws IOException
    {
        DataInputStream d = new DataInputStream(in);
        byte[] magic = new byte[MAGIC_LEN];
        d.readFully(magic, 0, MAGIC_LEN);
        int version = d.readInt();
        int compatibleVersion = d.readInt();
        return new SnappyCodec(magic, version, compatibleVersion);
    }
}