/*
* Copyright (C) 2012, 2016 higherfrequencytrading.com
* Copyright (C) 2016 Roman Leventov
*
* This program 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 3 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.openhft.chronicle.map;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.hash.serialization.BytesReader;
import net.openhft.chronicle.hash.serialization.BytesWriter;
import net.openhft.chronicle.hash.serialization.impl.EnumMarshallable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.lang.reflect.Constructor;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
/**
* Created by peter on 24/10/14.
*/
public final class DeflatorStringMarshaller implements BytesReader<CharSequence>,
BytesWriter<CharSequence>, EnumMarshallable<DeflatorStringMarshaller> {
public static final DeflatorStringMarshaller INSTANCE = new DeflatorStringMarshaller();
private DeflatorStringMarshaller() {}
private static final StringFactory STRING_FACTORY = getStringFactory();
private static final int NULL_LENGTH = -1;
private static StringFactory getStringFactory() {
try {
return new StringFactory17();
} catch (Exception e) {
// do nothing
}
try {
return new StringFactory16();
} catch (Exception e) {
// no more alternatives
throw new AssertionError(e);
}
}
@Override
public void write(Bytes out, @NotNull CharSequence s) {
if (s == null) {
out.writeStopBit(NULL_LENGTH);
return;
} else if (s.length() == 0) {
out.writeStopBit(0);
return;
}
// write the total length.
int length = s.length();
out.writeStopBit(length);
long position = out.writePosition();
out.writeInt(0);
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(
new DeflaterOutputStream(out.outputStream())));
try {
for (int i = 0; i < s.length(); i++) {
dos.write(s.charAt(i));
}
dos.close();
} catch (IOException e) {
throw new IORuntimeException(e);
}
out.writeInt(position, (int) (out.writePosition() - position - 4));
}
@NotNull
@Override
public CharSequence read(Bytes bytes, @Nullable CharSequence ignored) {
long size = bytes.readStopBit();
if (size == NULL_LENGTH)
return null;
if (size < 0 || size > Integer.MAX_VALUE)
throw new IllegalStateException("Invalid length: " + size);
if (size == 0)
return "";
char[] chars = new char[(int) size];
int length = bytes.readInt();
long limit = bytes.readLimit();
try {
bytes.readLimit(bytes.readPosition() + length);
DataInputStream dis = new DataInputStream(new BufferedInputStream(
new InflaterInputStream(bytes.inputStream())));
for (int i = 0; i < size; i++)
chars[i] = (char) (dis.readByte() & 0xff);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
bytes.readPosition(bytes.readLimit());
bytes.readLimit(limit);
}
try {
return STRING_FACTORY.fromChars(chars);
} catch (Exception e) {
throw new AssertionError(e);
}
}
@Override
public DeflatorStringMarshaller readResolve() {
return INSTANCE;
}
private static abstract class StringFactory {
abstract String fromChars(char[] chars) throws Exception;
}
private static final class StringFactory16 extends StringFactory {
private final Constructor<String> constructor;
private StringFactory16() throws Exception {
constructor = String.class.getDeclaredConstructor(int.class,
int.class, char[].class);
constructor.setAccessible(true);
}
@Override
String fromChars(char[] chars) throws Exception {
return constructor.newInstance(0, chars.length, chars);
}
}
private static final class StringFactory17 extends StringFactory {
private final Constructor<String> constructor;
private StringFactory17() throws Exception {
constructor = String.class.getDeclaredConstructor(char[].class, boolean.class);
constructor.setAccessible(true);
}
@Override
String fromChars(char[] chars) throws Exception {
return constructor.newInstance(chars, true);
}
}
}