CompressorDecompressor.java

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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.
 *
 */
package androidx.test.espresso.web.assertion;

import androidx.annotation.VisibleForTesting;
import androidx.test.internal.util.LogUtil;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import kotlin.io.ByteStreamsKt;

/** Compresses an input source. By default this class uses the GZIP format for compression. */
final class CompressorDecompressor {
  private static final String TAG = "CompressorDecompressor";
  private static final Compressor DEFAULT_COMPRESSOR = new GZIPCompressor();
  private static final Decompressor DEFAULT_DECOMPRESSOR = new GZIPDecompressor();

  public static byte[] compress(byte[] source) throws IOException {
    long startTime = System.currentTimeMillis();
    byte[] compressed = DEFAULT_COMPRESSOR.compress(source);
    long endTime = System.currentTimeMillis();
    LogUtil.logDebugWithProcess(
        TAG,
        "Compressed input with size %d (bytes) to output with size %d (bytes). Compression factor: "
            + "%f (%d bytes). Total time %d ms",
        source.length,
        compressed.length,
        (float) compressed.length / (float) source.length,
        Math.abs(compressed.length - source.length),
        endTime - startTime);
    return compressed;
  }

  public static byte[] decompress(byte[] bytes) throws IOException {
    long startTime = System.currentTimeMillis();
    byte[] decompressed = DEFAULT_DECOMPRESSOR.decompress(bytes);
    long endTime = System.currentTimeMillis();
    LogUtil.logDebugWithProcess(
        TAG,
        "Decompressed input with size %d (bytes) to output with size %d (bytes). Total time %d ms",
        bytes.length,
        decompressed.length,
        endTime - startTime);
    return decompressed;
  }

  interface Compressor {
    byte[] compress(byte[] bytes) throws IOException;
  }

  interface Decompressor {
    byte[] decompress(byte[] bytes) throws IOException;
  }

  @VisibleForTesting
  static final class GZIPCompressor implements Compressor {
    @Override
    public byte[] compress(byte[] bytes) throws IOException {
      ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
      GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
      try {
        ByteStreamsKt.copyTo(new ByteArrayInputStream(bytes), gzipOutputStream, bytes.length);
      } finally {
        if (gzipOutputStream != null) {
          gzipOutputStream.close();
        }
      }
      return byteArrayOutputStream.toByteArray();
    }
  }

  @VisibleForTesting
  static final class GZIPDecompressor implements Decompressor {
    @Override
    public byte[] decompress(byte[] bytes) throws IOException {
      ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
      GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);
      try {
        return ByteStreamsKt.readBytes(gzipInputStream);
      } finally {
        if (gzipInputStream != null) {
          gzipInputStream.close();
        }
      }
    }
  }
}