1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.commons.compress.archivers.sevenz;
20
21 import java.io.IOException;
22 import java.util.Collections;
23 import java.util.LinkedList;
24
25 import org.apache.commons.lang3.ArrayUtils;
26
27 /**
28 * The unit of solid compression.
29 */
30 final class Folder {
31
32 static final Folder[] EMPTY_FOLDER_ARRAY = {};
33
34 /** List of coders used in this folder, for example one for compression, one for encryption. */
35 Coder[] coders;
36
37 /**
38 * Total number of input streams across all coders. This field is currently unused but technically part of the 7z API.
39 */
40 long totalInputStreams;
41
42 /** Total number of output streams across all coders. */
43 long totalOutputStreams;
44
45 /** Mapping between input and output streams. */
46 BindPair[] bindPairs;
47
48 /** Indices of input streams, one per input stream not listed in bindPairs. */
49 long[] packedStreams;
50
51 /** Unpack sizes, per each output stream. */
52 long[] unpackSizes;
53
54 /** Whether the folder has a CRC. */
55 boolean hasCrc;
56
57 /** The CRC, if present. */
58 long crc;
59
60 /**
61 * The number of unpack substreams, product of the number of output streams and the number of non-empty files in this folder.
62 */
63 int numUnpackSubStreams;
64
65 int findBindPairForInStream(final int index) {
66 if (bindPairs != null) {
67 for (int i = 0; i < bindPairs.length; i++) {
68 if (bindPairs[i].inIndex == index) {
69 return i;
70 }
71 }
72 }
73 return -1;
74 }
75
76 int findBindPairForOutStream(final int index) {
77 if (bindPairs != null) {
78 for (int i = 0; i < bindPairs.length; i++) {
79 if (bindPairs[i].outIndex == index) {
80 return i;
81 }
82 }
83 }
84 return -1;
85 }
86
87 /**
88 * Sorts Coders using bind pairs.
89 * <p>
90 * The first coder reads from the packed stream (we currently only support single input stream decoders), the second reads from the output of the first and
91 * so on.
92 * </p>
93 */
94 Iterable<Coder> getOrderedCoders() throws IOException {
95 if (ArrayUtils.isEmpty(packedStreams) || ArrayUtils.isEmpty(coders)) {
96 return Collections.emptyList();
97 }
98 final LinkedList<Coder> list = new LinkedList<>();
99 int current = (int) packedStreams[0]; // more that 2^31 coders?
100 while (current >= 0 && current < coders.length) {
101 if (list.contains(coders[current])) {
102 throw new IOException("folder uses the same coder more than once in coder chain");
103 }
104 list.addLast(coders[current]);
105 final int pair = findBindPairForOutStream(current);
106 current = pair != -1 ? (int) bindPairs[pair].inIndex : -1;
107 }
108 return list;
109 }
110
111 long getUnpackSize() {
112 if (totalOutputStreams == 0) {
113 return 0;
114 }
115 for (int i = (int) totalOutputStreams - 1; i >= 0; i--) {
116 if (findBindPairForOutStream(i) < 0) {
117 return unpackSizes[i];
118 }
119 }
120 return 0;
121 }
122
123 long getUnpackSizeForCoder(final Coder coder) {
124 if (coders != null) {
125 for (int i = 0; i < coders.length; i++) {
126 if (coders[i] == coder) {
127 return unpackSizes[i];
128 }
129 }
130 }
131 return 0;
132 }
133
134 @Override
135 public String toString() {
136 return "Folder with " + coders.length + " coders, " + totalInputStreams + " input streams, " + totalOutputStreams + " output streams, "
137 + bindPairs.length + " bind pairs, " + packedStreams.length + " packed streams, " + unpackSizes.length + " unpack sizes, "
138 + (hasCrc ? "with CRC " + crc : "without CRC") + " and " + numUnpackSubStreams + " unpack streams";
139 }
140 }