190 def write_stream(stream: OutputStream): Unit = stream.write(bytes, offset, length) |
190 def write_stream(stream: OutputStream): Unit = stream.write(bytes, offset, length) |
191 |
191 |
192 |
192 |
193 /* XZ / Zstd data compression */ |
193 /* XZ / Zstd data compression */ |
194 |
194 |
195 private def detect_xz: Boolean = |
195 def detect_xz: Boolean = |
196 length >= 6 && |
196 length >= 6 && |
197 bytes(offset) == 0xFD.toByte && |
197 bytes(offset) == 0xFD.toByte && |
198 bytes(offset + 1) == 0x37.toByte && |
198 bytes(offset + 1) == 0x37.toByte && |
199 bytes(offset + 2) == 0x7A.toByte && |
199 bytes(offset + 2) == 0x7A.toByte && |
200 bytes(offset + 3) == 0x58.toByte && |
200 bytes(offset + 3) == 0x58.toByte && |
201 bytes(offset + 4) == 0x5A.toByte && |
201 bytes(offset + 4) == 0x5A.toByte && |
202 bytes(offset + 5) == 0x00.toByte |
202 bytes(offset + 5) == 0x00.toByte |
203 |
203 |
204 private def detect_zstd: Boolean = |
204 def detect_zstd: Boolean = |
205 length >= 4 && |
205 length >= 4 && |
206 bytes(offset) == 0x28.toByte && |
206 bytes(offset) == 0x28.toByte && |
207 bytes(offset + 1) == 0xB5.toByte && |
207 bytes(offset + 1) == 0xB5.toByte && |
208 bytes(offset + 2) == 0x2F.toByte && |
208 bytes(offset + 2) == 0x2F.toByte && |
209 bytes(offset + 3) == 0xFD.toByte |
209 bytes(offset + 3) == 0xFD.toByte |
210 |
210 |
211 private def detect_error(name: String = ""): Nothing = |
211 def uncompress_xz(cache: Compress.Cache = Compress.Cache.none): Bytes = |
212 error("Cannot detect compression scheme" + (if (name.isEmpty) "" else " " + name)) |
212 using(new xz.XZInputStream(stream(), cache.for_xz))(Bytes.read_stream(_, hint = length)) |
|
213 |
|
214 def uncompress_zstd(cache: Compress.Cache = Compress.Cache.none): Bytes = { |
|
215 Zstd.init() |
|
216 val n = zstd.Zstd.decompressedSize(bytes, offset, length) |
|
217 if (n > 0 && n < Integer.MAX_VALUE) { |
|
218 Bytes(zstd.Zstd.decompress(array, n.toInt)) |
|
219 } |
|
220 else { |
|
221 using(new zstd.ZstdInputStream(stream(), cache.for_zstd))(Bytes.read_stream(_, hint = length)) |
|
222 } |
|
223 } |
213 |
224 |
214 def uncompress(cache: Compress.Cache = Compress.Cache.none): Bytes = |
225 def uncompress(cache: Compress.Cache = Compress.Cache.none): Bytes = |
215 using( |
226 if (detect_xz) uncompress_xz(cache = cache) |
216 if (detect_xz) new xz.XZInputStream(stream(), cache.for_xz) |
227 else if (detect_zstd) uncompress_zstd(cache = cache) |
217 else if (detect_zstd) { |
228 else error("Cannot detect compression scheme") |
218 Zstd.init() |
|
219 new zstd.ZstdInputStream(stream(), cache.for_zstd) |
|
220 } |
|
221 else detect_error() |
|
222 )(Bytes.read_stream(_, hint = length)) |
|
223 |
|
224 def uncompress_xz(cache: Compress.Cache = Compress.Cache.none): Bytes = |
|
225 if (detect_xz) uncompress(cache = cache) else detect_error("XZ") |
|
226 |
|
227 def uncompress_zstd(cache: Compress.Cache = Compress.Cache.none): Bytes = |
|
228 if (detect_zstd) uncompress(cache = cache) else detect_error("Zstd") |
|
229 |
229 |
230 def compress( |
230 def compress( |
231 options: Compress.Options = Compress.Options(), |
231 options: Compress.Options = Compress.Options(), |
232 cache: Compress.Cache = Compress.Cache.none |
232 cache: Compress.Cache = Compress.Cache.none |
233 ): Bytes = { |
233 ): Bytes = { |
234 val result = new ByteArrayOutputStream(length) |
234 options match { |
235 using( |
235 case options_xz: Compress.Options_XZ => |
236 options match { |
236 val result = new ByteArrayOutputStream(length) |
237 case options_xz: Compress.Options_XZ => |
237 using(new xz.XZOutputStream(result, options_xz.make, cache.for_xz))(write_stream) |
238 new xz.XZOutputStream(result, options_xz.make, cache.for_xz) |
238 new Bytes(result.toByteArray, 0, result.size) |
239 case options_zstd: Compress.Options_Zstd => |
239 case options_zstd: Compress.Options_Zstd => |
240 Zstd.init() |
240 Zstd.init() |
241 new zstd.ZstdOutputStream(result, cache.for_zstd, options_zstd.level) |
241 Bytes(zstd.Zstd.compress(array, options_zstd.level)) |
242 })(write_stream) |
242 } |
243 new Bytes(result.toByteArray, 0, result.size) |
|
244 } |
243 } |
245 |
244 |
246 def maybe_compress( |
245 def maybe_compress( |
247 options: Compress.Options = Compress.Options(), |
246 options: Compress.Options = Compress.Options(), |
248 cache: Compress.Cache = Compress.Cache.none |
247 cache: Compress.Cache = Compress.Cache.none |