Skip to content

Commit

Permalink
Add CompressFilterOutputStream and refactor to use
Browse files Browse the repository at this point in the history
  • Loading branch information
garydgregory committed Dec 11, 2024
1 parent 794696e commit 0d957a8
Show file tree
Hide file tree
Showing 39 changed files with 325 additions and 212 deletions.
1 change: 1 addition & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ The <action> type attribute can be add,update,fix,remove.
<action type="add" dev="ggregory" due-to="Gary Gregory">Add GzipParameters.toString().</action>
<action type="add" dev="ggregory" issue="COMPRESS-638" due-to="vincexjl, Gary Gregory, Piotr P. Karwasz">Add GzipParameters.setFileNameCharset(Charset) and getFileNameCharset() to override the default ISO-8859-1 Charset #602.</action>
<action type="add" dev="ggregory" due-to="ddeschenes-1, Gary Gregory">Add support for gzip extra subfields, see GzipParameters.setExtra(HeaderExtraField) #604.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add CompressFilterOutputStream and refactor to use.</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump org.apache.commons:commons-parent from 72 to 78 #563, #567, #574, #582, #587, #595.</action>
<action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump com.github.luben:zstd-jni from 1.5.6-4 to 1.5.6-8 #565, #578, #601, #616.</action>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.commons.compress;

import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

/**
* Abstracts classes that compress or archive an output stream.
*
* @param <T> The underlying {@link OutputStream} type.
* @since 1.28.0
*/
public abstract class CompressFilterOutputStream<T extends OutputStream> extends FilterOutputStream {

/**
* Writes and filters the bytes from the specified String to this output stream using the given Charset.
*
* @param os the target output stream.
* @param data the data.
* @param charset The {@link Charset} to be used to encode the {@code String}
* @return the ASCII bytes.
* @exception IOException if an I/O error occurs.
* @see OutputStream#write(byte[])
*/
private static byte[] write(final OutputStream os, final String data, final Charset charset) throws IOException {
final byte[] bytes = data.getBytes(charset);
os.write(bytes);
return bytes;
}

/**
* Constructs a new instance without a backing {@link OutputStream}.
* <p>
* You must initialize {@code this.out} after construction.
* </p>
*/
public CompressFilterOutputStream() {
super(null);
}

/**
* Creates an output stream filter built on top of the specified underlying {@link OutputStream}.
*
* @param out the underlying output stream to be assigned to the field {@code this.out} for later use, or {@code null} if this instance is to be created
* without an underlying stream.
*/
public CompressFilterOutputStream(final T out) {
super(out);
}

/**
* Gets the underlying output stream.
*
* @return the underlying output stream.
*/
@SuppressWarnings("unchecked")
protected T out() {
return (T) out;
}

/**
* Writes all bytes from a file this output stream.
*
* @param file the path to the source file.
* @return the number of bytes read or written.
* @throws IOException if an I/O error occurs when reading or writing.
*/
public long write(final File file) throws IOException {
return write(file.toPath());
}

/**
* Writes all bytes from a file to this output stream.
*
* @param path the path to the source file.
* @return the number of bytes read or written.
* @throws IOException if an I/O error occurs when reading or writing.
*/
public long write(final Path path) throws IOException {
return Files.copy(path, this);
}

/**
* Writes and filters the ASCII bytes from the specified String to this output stream.
*
* @param data the data.
* @return the ASCII bytes.
* @exception IOException if an I/O error occurs.
* @see OutputStream#write(byte[])
*/
public byte[] writeUsAscii(final String data) throws IOException {
return write(this, data, StandardCharsets.US_ASCII);
}

/**
* Writes the raw ASCII bytes from the specified String to this output stream.
*
* @param data the data.
* @return the ASCII bytes.
* @exception IOException if an I/O error occurs.
* @see OutputStream#write(byte[])
*/
public byte[] writeUsAsciiRaw(final String data) throws IOException {
return write(out, data, StandardCharsets.US_ASCII);
}

/**
* Writes and filters the UTF-8 bytes from the specified String to this output stream.
*
* @param data the data.
* @return the ASCII bytes.
* @exception IOException if an I/O error occurs.
* @see OutputStream#write(byte[])
*/
public byte[] writeUtf8(final String data) throws IOException {
return write(this, data, StandardCharsets.UTF_8);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
package org.apache.commons.compress.archivers;

import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.LinkOption;
import java.nio.file.Path;

import org.apache.commons.compress.CompressFilterOutputStream;

/**
* Archive output stream implementations are expected to override the {@link #write(byte[], int, int)} method to improve performance. They should also override
* {@link #close()} to ensure that any necessary trailers are added.
Expand All @@ -49,7 +50,7 @@
*
* @param <E> The type of {@link ArchiveEntry} consumed.
*/
public abstract class ArchiveOutputStream<E extends ArchiveEntry> extends FilterOutputStream {
public abstract class ArchiveOutputStream<E extends ArchiveEntry> extends CompressFilterOutputStream<OutputStream> {

static final int BYTE_MASK = 0xFF;

Expand All @@ -76,7 +77,7 @@ public abstract class ArchiveOutputStream<E extends ArchiveEntry> extends Filter
* </p>
*/
public ArchiveOutputStream() {
super(null);
super();
}

/**
Expand Down Expand Up @@ -202,6 +203,7 @@ public E createArchiveEntry(final Path inputPath, final String entryName, final
*
* @throws IOException Maybe thrown by subclasses if the user forgets to close the entry.
*/
@SuppressWarnings("unused") // for subclasses
public void finish() throws IOException {
finished = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,13 @@
*/
package org.apache.commons.compress.archivers.ar;

import static java.nio.charset.StandardCharsets.US_ASCII;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.LinkOption;
import java.nio.file.Path;

import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.utils.ArchiveUtils;

/**
* Implements the "ar" archive format as an output stream.
Expand Down Expand Up @@ -164,13 +161,11 @@ public void write(final byte[] b, final int off, final int len) throws IOExcepti
}

private int write(final String data) throws IOException {
final byte[] bytes = data.getBytes(US_ASCII);
write(bytes);
return bytes.length;
return writeUsAscii(data).length;
}

private void writeArchiveHeader() throws IOException {
out.write(ArchiveUtils.toAsciiBytes(ArArchiveEntry.HEADER));
private byte[] writeArchiveHeader() throws IOException {
return writeUsAscii(ArArchiveEntry.HEADER);
}

private int writeEntryHeader(final ArArchiveEntry entry) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipEncoding;
import org.apache.commons.compress.archivers.zip.ZipEncodingHelper;
import org.apache.commons.compress.utils.ArchiveUtils;

/**
* CpioArchiveOutputStream is a stream for writing CPIO streams. All formats of CPIO are supported (old ASCII, old binary, new portable format and the new
Expand Down Expand Up @@ -271,8 +270,7 @@ public void finish() throws IOException {

private void pad(final int count) throws IOException {
if (count > 0) {
final byte[] buff = new byte[count];
out.write(buff);
out.write(new byte[count]);
count(count);
}
}
Expand Down Expand Up @@ -365,8 +363,7 @@ private void writeAsciiLong(final long number, final int length, final int radix
} else {
tmpStr = tmp.substring(tmp.length() - length);
}
final byte[] b = ArchiveUtils.toAsciiBytes(tmpStr);
out.write(b);
final byte[] b = writeUsAsciiRaw(tmpStr);
count(b.length);
}

Expand All @@ -391,17 +388,17 @@ private void writeCString(final byte[] str) throws IOException {
private void writeHeader(final CpioArchiveEntry e) throws IOException {
switch (e.getFormat()) {
case FORMAT_NEW:
out.write(ArchiveUtils.toAsciiBytes(MAGIC_NEW));
writeUsAsciiRaw(MAGIC_NEW);
count(6);
writeNewEntry(e);
break;
case FORMAT_NEW_CRC:
out.write(ArchiveUtils.toAsciiBytes(MAGIC_NEW_CRC));
writeUsAsciiRaw(MAGIC_NEW_CRC);
count(6);
writeNewEntry(e);
break;
case FORMAT_OLD_ASCII:
out.write(ArchiveUtils.toAsciiBytes(MAGIC_OLD_ASCII));
writeUsAsciiRaw(MAGIC_OLD_ASCII);
count(6);
writeOldAsciiEntry(e);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,12 @@ public class Archiver {

private static class ArchiverFileVisitor<O extends ArchiveOutputStream<E>, E extends ArchiveEntry> extends SimpleFileVisitor<Path> {

private final O target;
private final O outputStream;
private final Path directory;
private final LinkOption[] linkOptions;

private ArchiverFileVisitor(final O target, final Path directory, final LinkOption... linkOptions) {
this.target = target;
this.outputStream = target;
this.directory = directory;
this.linkOptions = linkOptions == null ? IOUtils.EMPTY_LINK_OPTIONS : linkOptions.clone();
}
Expand All @@ -74,13 +74,13 @@ protected FileVisitResult visit(final Path path, final BasicFileAttributes attrs
Objects.requireNonNull(attrs);
final String name = directory.relativize(path).toString().replace('\\', '/');
if (!name.isEmpty()) {
final E archiveEntry = target.createArchiveEntry(path, isFile || name.endsWith("/") ? name : name + "/", linkOptions);
target.putArchiveEntry(archiveEntry);
final E archiveEntry = outputStream.createArchiveEntry(path, isFile || name.endsWith("/") ? name : name + "/", linkOptions);
outputStream.putArchiveEntry(archiveEntry);
if (isFile) {
// Refactor this as a BiConsumer on Java 8
Files.copy(path, target);
// Refactor this as a BiConsumer on Java 8?
outputStream.write(path);
}
target.closeArchiveEntry();
outputStream.closeArchiveEntry();
}
return FileVisitResult.CONTINUE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@
*/
package org.apache.commons.compress.compressors;

import java.io.FilterOutputStream;
import java.io.OutputStream;

import org.apache.commons.compress.CompressFilterOutputStream;

/**
* Abstracts all classes that compress an output stream.
*
* @param <T> The underlying {@link OutputStream} type.
*/
public abstract class CompressorOutputStream<T extends OutputStream> extends FilterOutputStream {
public abstract class CompressorOutputStream<T extends OutputStream> extends CompressFilterOutputStream<T> {

/**
* Constructs a new instance without a backing {@link OutputStream}.
Expand All @@ -35,7 +36,7 @@ public abstract class CompressorOutputStream<T extends OutputStream> extends Fil
* </p>
*/
public CompressorOutputStream() {
super(null);
super();
}

/**
Expand All @@ -49,14 +50,4 @@ public CompressorOutputStream(final T out) {
super(out);
}

/**
* Gets the underlying output stream.
*
* @return the underlying output stream.
* @since 1.27.0
*/
@SuppressWarnings("unchecked")
protected T out() {
return (T) out;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public void write(final int b) throws IOException {
* @param charset Specifies the Charset to use.
* @throws IOException if an I/O error occurs.
*/
private void write(final String value, final Charset charset) throws IOException {
private void writeC(final String value, final Charset charset) throws IOException {
if (value != null) {
out.write(value.getBytes(charset));
out.write(0);
Expand Down Expand Up @@ -195,8 +195,8 @@ private void writeHeader(final GzipParameters parameters) throws IOException {
out.write(extra.length >>> 8 & 0xff);
out.write(extra);
}
write(fileName, parameters.getFileNameCharset());
write(comment, parameters.getFileNameCharset());
writeC(fileName, parameters.getFileNameCharset());
writeC(comment, parameters.getFileNameCharset());
}

private void writeTrailer() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ private <O extends ArchiveOutputStream<E>, E extends ArchiveEntry> void addArchi
throws IOException, FileNotFoundException {
final E entry = outputStream.createArchiveEntry(inputFile, fileName);
outputStream.putArchiveEntry(entry);
Files.copy(inputFile.toPath(), outputStream);
outputStream.write(inputFile);
outputStream.closeArchiveEntry();
archiveList.add(fileName);
}
Expand Down
Loading

0 comments on commit 0d957a8

Please sign in to comment.