GzipOutputStream.java 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. /* _____ _
  2. * |_ _| |_ _ _ ___ ___ _ __ __ _
  3. * | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. * |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. *
  6. * Threema for Android
  7. * Copyright (c) 2018-2022 Threema GmbH
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License, version 3,
  11. * as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. */
  21. package ch.threema.app.utils;
  22. import static java.util.zip.GZIPInputStream.GZIP_MAGIC;
  23. import java.io.IOException;
  24. import java.io.OutputStream;
  25. import java.util.zip.CRC32;
  26. import java.util.zip.Deflater;
  27. import java.util.zip.DeflaterOutputStream;
  28. /**
  29. * Copy of AOSP's GZIPOutputStream exposing a parameter for Deflator's compression level
  30. */
  31. public class GzipOutputStream extends DeflaterOutputStream {
  32. protected CRC32 crc = new CRC32();
  33. private final static int TRAILER_SIZE = 8;
  34. public GzipOutputStream(OutputStream out, int level, int size, boolean syncFlush) throws IOException {
  35. super(out, new Deflater(level, true), size, syncFlush);
  36. writeHeader();
  37. crc.reset();
  38. }
  39. public synchronized void write(byte[] buf, int off, int len) throws IOException {
  40. super.write(buf, off, len);
  41. crc.update(buf, off, len);
  42. }
  43. public void finish() throws IOException {
  44. if (!def.finished()) {
  45. def.finish();
  46. while (!def.finished()) {
  47. int len = def.deflate(buf, 0, buf.length);
  48. if (def.finished() && len <= buf.length - TRAILER_SIZE) {
  49. writeTrailer(buf, len);
  50. len = len + TRAILER_SIZE;
  51. out.write(buf, 0, len);
  52. return;
  53. }
  54. if (len > 0)
  55. out.write(buf, 0, len);
  56. }
  57. byte[] trailer = new byte[TRAILER_SIZE];
  58. writeTrailer(trailer, 0);
  59. out.write(trailer);
  60. }
  61. }
  62. private void writeHeader() throws IOException {
  63. out.write(new byte[] {
  64. (byte) GZIP_MAGIC,
  65. (byte)(GZIP_MAGIC >> 8),
  66. Deflater.DEFLATED,
  67. 0,0,0,0,0,0,0
  68. });
  69. }
  70. private void writeTrailer(byte[] buf, int offset) {
  71. writeInt((int)crc.getValue(), buf, offset);
  72. writeInt(def.getTotalIn(), buf, offset + 4);
  73. }
  74. private void writeInt(int i, byte[] buf, int offset) {
  75. writeShort(i & 0xffff, buf, offset);
  76. writeShort((i >> 16) & 0xffff, buf, offset + 2);
  77. }
  78. private void writeShort(int s, byte[] buf, int offset) {
  79. buf[offset] = (byte)(s & 0xff);
  80. buf[offset + 1] = (byte)((s >> 8) & 0xff);
  81. }
  82. }