diff options
Diffstat (limited to '')
-rw-r--r-- | C/XzIn.c | 265 |
1 files changed, 155 insertions, 110 deletions
@@ -1,38 +1,39 @@ | |||
1 | /* XzIn.c - Xz input | 1 | /* XzIn.c - Xz input |
2 | 2023-09-07 : Igor Pavlov : Public domain */ | 2 | : Igor Pavlov : Public domain */ |
3 | 3 | ||
4 | #include "Precomp.h" | 4 | #include "Precomp.h" |
5 | 5 | ||
6 | #include <string.h> | 6 | #include <string.h> |
7 | 7 | ||
8 | #include "7zCrc.h" | 8 | #include "7zCrc.h" |
9 | #include "CpuArch.h" | ||
10 | #include "Xz.h" | 9 | #include "Xz.h" |
10 | #include "CpuArch.h" | ||
11 | 11 | ||
12 | /* | 12 | #define XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(p) \ |
13 | #define XZ_FOOTER_SIG_CHECK(p) (memcmp((p), XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0) | 13 | (GetUi16a((const Byte *)(const void *)(p) + 10) == \ |
14 | */ | 14 | (XZ_FOOTER_SIG_0 | (XZ_FOOTER_SIG_1 << 8))) |
15 | #define XZ_FOOTER_SIG_CHECK(p) ((p)[0] == XZ_FOOTER_SIG_0 && (p)[1] == XZ_FOOTER_SIG_1) | ||
16 | |||
17 | 15 | ||
18 | SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStreamPtr inStream) | 16 | SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStreamPtr inStream) |
19 | { | 17 | { |
20 | Byte sig[XZ_STREAM_HEADER_SIZE]; | 18 | UInt32 data32[XZ_STREAM_HEADER_SIZE / 4]; |
21 | size_t processedSize = XZ_STREAM_HEADER_SIZE; | 19 | size_t processedSize = XZ_STREAM_HEADER_SIZE; |
22 | RINOK(SeqInStream_ReadMax(inStream, sig, &processedSize)) | 20 | RINOK(SeqInStream_ReadMax(inStream, data32, &processedSize)) |
23 | if (processedSize != XZ_STREAM_HEADER_SIZE | 21 | if (processedSize != XZ_STREAM_HEADER_SIZE |
24 | || memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0) | 22 | || memcmp(data32, XZ_SIG, XZ_SIG_SIZE) != 0) |
25 | return SZ_ERROR_NO_ARCHIVE; | 23 | return SZ_ERROR_NO_ARCHIVE; |
26 | return Xz_ParseHeader(p, sig); | 24 | return Xz_ParseHeader(p, (const Byte *)(const void *)data32); |
27 | } | 25 | } |
28 | 26 | ||
29 | #define READ_VARINT_AND_CHECK(buf, pos, size, res) \ | 27 | #define READ_VARINT_AND_CHECK(buf, size, res) \ |
30 | { const unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ | 28 | { const unsigned s = Xz_ReadVarInt(buf, size, res); \ |
31 | if (s == 0) return SZ_ERROR_ARCHIVE; \ | 29 | if (s == 0) return SZ_ERROR_ARCHIVE; \ |
32 | pos += s; } | 30 | size -= s; \ |
31 | buf += s; \ | ||
32 | } | ||
33 | 33 | ||
34 | SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes) | 34 | SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes) |
35 | { | 35 | { |
36 | MY_ALIGN(4) | ||
36 | Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; | 37 | Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; |
37 | unsigned headerSize; | 38 | unsigned headerSize; |
38 | *headerSizeRes = 0; | 39 | *headerSizeRes = 0; |
@@ -57,8 +58,12 @@ SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, | |||
57 | return XzBlock_Parse(p, header); | 58 | return XzBlock_Parse(p, header); |
58 | } | 59 | } |
59 | 60 | ||
61 | |||
60 | #define ADD_SIZE_CHECK(size, val) \ | 62 | #define ADD_SIZE_CHECK(size, val) \ |
61 | { const UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; } | 63 | { const UInt64 newSize = size + (val); \ |
64 | if (newSize < size) return XZ_SIZE_OVERFLOW; \ | ||
65 | size = newSize; \ | ||
66 | } | ||
62 | 67 | ||
63 | UInt64 Xz_GetUnpackSize(const CXzStream *p) | 68 | UInt64 Xz_GetUnpackSize(const CXzStream *p) |
64 | { | 69 | { |
@@ -82,76 +87,85 @@ UInt64 Xz_GetPackSize(const CXzStream *p) | |||
82 | return size; | 87 | return size; |
83 | } | 88 | } |
84 | 89 | ||
85 | /* | ||
86 | SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStreamPtr inStream) | ||
87 | { | ||
88 | return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f)); | ||
89 | } | ||
90 | */ | ||
91 | 90 | ||
92 | static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc) | 91 | // input; |
92 | // CXzStream (p) is empty object. | ||
93 | // size != 0 | ||
94 | // (size & 3) == 0 | ||
95 | // (buf) is aligned for at least 4 bytes. | ||
96 | // output: | ||
97 | // p->numBlocks is number of allocated items in p->blocks | ||
98 | // p->blocks[*] values must be ignored, if function returns error. | ||
99 | static SRes Xz_ParseIndex(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc) | ||
93 | { | 100 | { |
94 | size_t numBlocks, pos = 1; | 101 | size_t numBlocks; |
95 | UInt32 crc; | ||
96 | |||
97 | if (size < 5 || buf[0] != 0) | 102 | if (size < 5 || buf[0] != 0) |
98 | return SZ_ERROR_ARCHIVE; | 103 | return SZ_ERROR_ARCHIVE; |
99 | |||
100 | size -= 4; | 104 | size -= 4; |
101 | crc = CrcCalc(buf, size); | 105 | { |
102 | if (crc != GetUi32(buf + size)) | 106 | const UInt32 crc = CrcCalc(buf, size); |
103 | return SZ_ERROR_ARCHIVE; | 107 | if (crc != GetUi32a(buf + size)) |
104 | 108 | return SZ_ERROR_ARCHIVE; | |
109 | } | ||
110 | buf++; | ||
111 | size--; | ||
105 | { | 112 | { |
106 | UInt64 numBlocks64; | 113 | UInt64 numBlocks64; |
107 | READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64) | 114 | READ_VARINT_AND_CHECK(buf, size, &numBlocks64) |
108 | numBlocks = (size_t)numBlocks64; | 115 | // (numBlocks64) is 63-bit value, so we can calculate (numBlocks64 * 2): |
109 | if (numBlocks != numBlocks64 || numBlocks * 2 > size) | 116 | if (numBlocks64 * 2 > size) |
110 | return SZ_ERROR_ARCHIVE; | 117 | return SZ_ERROR_ARCHIVE; |
118 | if (numBlocks64 >= ((size_t)1 << (sizeof(size_t) * 8 - 1)) / sizeof(CXzBlockSizes)) | ||
119 | return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE | ||
120 | numBlocks = (size_t)numBlocks64; | ||
111 | } | 121 | } |
112 | 122 | // Xz_Free(p, alloc); // it's optional, because (p) is empty already | |
113 | Xz_Free(p, alloc); | 123 | if (numBlocks) |
114 | if (numBlocks != 0) | ||
115 | { | 124 | { |
116 | size_t i; | 125 | CXzBlockSizes *blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); |
117 | p->numBlocks = numBlocks; | 126 | if (!blocks) |
118 | p->blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); | ||
119 | if (!p->blocks) | ||
120 | return SZ_ERROR_MEM; | 127 | return SZ_ERROR_MEM; |
121 | for (i = 0; i < numBlocks; i++) | 128 | p->blocks = blocks; |
129 | p->numBlocks = numBlocks; | ||
130 | // the caller will call Xz_Free() in case of error | ||
131 | do | ||
122 | { | 132 | { |
123 | CXzBlockSizes *block = &p->blocks[i]; | 133 | READ_VARINT_AND_CHECK(buf, size, &blocks->totalSize) |
124 | READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize) | 134 | READ_VARINT_AND_CHECK(buf, size, &blocks->unpackSize) |
125 | READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize) | 135 | if (blocks->totalSize == 0) |
126 | if (block->totalSize == 0) | ||
127 | return SZ_ERROR_ARCHIVE; | 136 | return SZ_ERROR_ARCHIVE; |
137 | blocks++; | ||
128 | } | 138 | } |
139 | while (--numBlocks); | ||
129 | } | 140 | } |
130 | while ((pos & 3) != 0) | 141 | if (size >= 4) |
131 | if (buf[pos++] != 0) | 142 | return SZ_ERROR_ARCHIVE; |
143 | while (size) | ||
144 | if (buf[--size]) | ||
132 | return SZ_ERROR_ARCHIVE; | 145 | return SZ_ERROR_ARCHIVE; |
133 | return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; | 146 | return SZ_OK; |
134 | } | 147 | } |
135 | 148 | ||
149 | |||
150 | /* | ||
136 | static SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc) | 151 | static SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc) |
137 | { | 152 | { |
138 | SRes res; | 153 | SRes res; |
139 | size_t size; | 154 | size_t size; |
140 | Byte *buf; | 155 | Byte *buf; |
141 | if (indexSize > ((UInt32)1 << 31)) | 156 | if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1))) |
142 | return SZ_ERROR_UNSUPPORTED; | 157 | return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE |
143 | size = (size_t)indexSize; | 158 | size = (size_t)indexSize; |
144 | if (size != indexSize) | ||
145 | return SZ_ERROR_UNSUPPORTED; | ||
146 | buf = (Byte *)ISzAlloc_Alloc(alloc, size); | 159 | buf = (Byte *)ISzAlloc_Alloc(alloc, size); |
147 | if (!buf) | 160 | if (!buf) |
148 | return SZ_ERROR_MEM; | 161 | return SZ_ERROR_MEM; |
149 | res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); | 162 | res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); |
150 | if (res == SZ_OK) | 163 | if (res == SZ_OK) |
151 | res = Xz_ReadIndex2(p, buf, size, alloc); | 164 | res = Xz_ParseIndex(p, buf, size, alloc); |
152 | ISzAlloc_Free(alloc, buf); | 165 | ISzAlloc_Free(alloc, buf); |
153 | return res; | 166 | return res; |
154 | } | 167 | } |
168 | */ | ||
155 | 169 | ||
156 | static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size) | 170 | static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size) |
157 | { | 171 | { |
@@ -160,84 +174,102 @@ static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, | |||
160 | /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */ | 174 | /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */ |
161 | } | 175 | } |
162 | 176 | ||
177 | |||
178 | /* | ||
179 | in: | ||
180 | (*startOffset) is position in (stream) where xz_stream must be finished. | ||
181 | out: | ||
182 | if returns SZ_OK, then (*startOffset) is position in stream that shows start of xz_stream. | ||
183 | */ | ||
163 | static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc) | 184 | static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc) |
164 | { | 185 | { |
165 | UInt64 indexSize; | 186 | #define TEMP_BUF_SIZE (1 << 10) |
166 | Byte buf[XZ_STREAM_FOOTER_SIZE]; | 187 | UInt32 buf32[TEMP_BUF_SIZE / 4]; |
167 | UInt64 pos = (UInt64)*startOffset; | 188 | UInt64 pos = (UInt64)*startOffset; |
168 | 189 | ||
169 | if ((pos & 3) != 0 || pos < XZ_STREAM_FOOTER_SIZE) | 190 | if ((pos & 3) || pos < XZ_STREAM_FOOTER_SIZE) |
170 | return SZ_ERROR_NO_ARCHIVE; | 191 | return SZ_ERROR_NO_ARCHIVE; |
171 | |||
172 | pos -= XZ_STREAM_FOOTER_SIZE; | 192 | pos -= XZ_STREAM_FOOTER_SIZE; |
173 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE)) | 193 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE)) |
174 | 194 | ||
175 | if (!XZ_FOOTER_SIG_CHECK(buf + 10)) | 195 | if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32)) |
176 | { | 196 | { |
177 | UInt32 total = 0; | ||
178 | pos += XZ_STREAM_FOOTER_SIZE; | 197 | pos += XZ_STREAM_FOOTER_SIZE; |
179 | |||
180 | for (;;) | 198 | for (;;) |
181 | { | 199 | { |
182 | size_t i; | 200 | // pos != 0 |
183 | #define TEMP_BUF_SIZE (1 << 10) | 201 | // (pos & 3) == 0 |
184 | Byte temp[TEMP_BUF_SIZE]; | 202 | size_t i = pos >= TEMP_BUF_SIZE ? TEMP_BUF_SIZE : (size_t)pos; |
185 | |||
186 | i = (pos > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)pos; | ||
187 | pos -= i; | 203 | pos -= i; |
188 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, temp, i)) | 204 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, i)) |
189 | total += (UInt32)i; | 205 | i /= 4; |
190 | for (; i != 0; i--) | 206 | do |
191 | if (temp[i - 1] != 0) | 207 | if (buf32[i - 1] != 0) |
192 | break; | 208 | break; |
193 | if (i != 0) | 209 | while (--i); |
194 | { | 210 | |
195 | if ((i & 3) != 0) | 211 | pos += i * 4; |
196 | return SZ_ERROR_NO_ARCHIVE; | 212 | #define XZ_STREAM_BACKWARD_READING_PAD_MAX (1 << 16) |
197 | pos += i; | 213 | // here we don't support rare case with big padding for xz stream. |
198 | break; | 214 | // so we have padding limit for backward reading. |
199 | } | 215 | if ((UInt64)*startOffset - pos > XZ_STREAM_BACKWARD_READING_PAD_MAX) |
200 | if (pos < XZ_STREAM_FOOTER_SIZE || total > (1 << 16)) | ||
201 | return SZ_ERROR_NO_ARCHIVE; | 216 | return SZ_ERROR_NO_ARCHIVE; |
217 | if (i) | ||
218 | break; | ||
202 | } | 219 | } |
203 | 220 | // we try to open xz stream after skipping zero padding. | |
221 | // ((UInt64)*startOffset == pos) is possible here! | ||
204 | if (pos < XZ_STREAM_FOOTER_SIZE) | 222 | if (pos < XZ_STREAM_FOOTER_SIZE) |
205 | return SZ_ERROR_NO_ARCHIVE; | 223 | return SZ_ERROR_NO_ARCHIVE; |
206 | pos -= XZ_STREAM_FOOTER_SIZE; | 224 | pos -= XZ_STREAM_FOOTER_SIZE; |
207 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE)) | 225 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf32, XZ_STREAM_FOOTER_SIZE)) |
208 | if (!XZ_FOOTER_SIG_CHECK(buf + 10)) | 226 | if (!XZ_FOOTER_12B_ALIGNED16_SIG_CHECK(buf32)) |
209 | return SZ_ERROR_NO_ARCHIVE; | 227 | return SZ_ERROR_NO_ARCHIVE; |
210 | } | 228 | } |
211 | 229 | ||
212 | p->flags = (CXzStreamFlags)GetBe16(buf + 8); | 230 | p->flags = (CXzStreamFlags)GetBe16a(buf32 + 2); |
213 | |||
214 | if (!XzFlags_IsSupported(p->flags)) | 231 | if (!XzFlags_IsSupported(p->flags)) |
215 | return SZ_ERROR_UNSUPPORTED; | 232 | return SZ_ERROR_UNSUPPORTED; |
216 | |||
217 | { | 233 | { |
218 | /* to eliminate GCC 6.3 warning: | 234 | /* to eliminate GCC 6.3 warning: |
219 | dereferencing type-punned pointer will break strict-aliasing rules */ | 235 | dereferencing type-punned pointer will break strict-aliasing rules */ |
220 | const Byte *buf_ptr = buf; | 236 | const UInt32 *buf_ptr = buf32; |
221 | if (GetUi32(buf_ptr) != CrcCalc(buf + 4, 6)) | 237 | if (GetUi32a(buf_ptr) != CrcCalc(buf32 + 1, 6)) |
222 | return SZ_ERROR_ARCHIVE; | 238 | return SZ_ERROR_ARCHIVE; |
223 | } | 239 | } |
224 | |||
225 | indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2; | ||
226 | |||
227 | if (pos < indexSize) | ||
228 | return SZ_ERROR_ARCHIVE; | ||
229 | |||
230 | pos -= indexSize; | ||
231 | RINOK(LookInStream_SeekTo(stream, pos)) | ||
232 | RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)) | ||
233 | |||
234 | { | 240 | { |
235 | UInt64 totalSize = Xz_GetPackSize(p); | 241 | const UInt64 indexSize = ((UInt64)GetUi32a(buf32 + 1) + 1) << 2; |
236 | if (totalSize == XZ_SIZE_OVERFLOW | 242 | if (pos < indexSize) |
237 | || totalSize >= ((UInt64)1 << 63) | ||
238 | || pos < totalSize + XZ_STREAM_HEADER_SIZE) | ||
239 | return SZ_ERROR_ARCHIVE; | 243 | return SZ_ERROR_ARCHIVE; |
240 | pos -= (totalSize + XZ_STREAM_HEADER_SIZE); | 244 | pos -= indexSize; |
245 | // v25.00: relaxed indexSize check. We allow big index table. | ||
246 | // if (indexSize > ((UInt32)1 << 31)) | ||
247 | if (indexSize >= ((size_t)1 << (sizeof(size_t) * 8 - 1))) | ||
248 | return SZ_ERROR_MEM; // SZ_ERROR_ARCHIVE | ||
249 | RINOK(LookInStream_SeekTo(stream, pos)) | ||
250 | // RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)) | ||
251 | { | ||
252 | SRes res; | ||
253 | const size_t size = (size_t)indexSize; | ||
254 | // if (size != indexSize) return SZ_ERROR_UNSUPPORTED; | ||
255 | Byte *buf = (Byte *)ISzAlloc_Alloc(alloc, size); | ||
256 | if (!buf) | ||
257 | return SZ_ERROR_MEM; | ||
258 | res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); | ||
259 | if (res == SZ_OK) | ||
260 | res = Xz_ParseIndex(p, buf, size, alloc); | ||
261 | ISzAlloc_Free(alloc, buf); | ||
262 | RINOK(res) | ||
263 | } | ||
264 | } | ||
265 | { | ||
266 | UInt64 total = Xz_GetPackSize(p); | ||
267 | if (total == XZ_SIZE_OVERFLOW || total >= ((UInt64)1 << 63)) | ||
268 | return SZ_ERROR_ARCHIVE; | ||
269 | total += XZ_STREAM_HEADER_SIZE; | ||
270 | if (pos < total) | ||
271 | return SZ_ERROR_ARCHIVE; | ||
272 | pos -= total; | ||
241 | RINOK(LookInStream_SeekTo(stream, pos)) | 273 | RINOK(LookInStream_SeekTo(stream, pos)) |
242 | *startOffset = (Int64)pos; | 274 | *startOffset = (Int64)pos; |
243 | } | 275 | } |
@@ -246,7 +278,6 @@ static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startO | |||
246 | CSecToRead secToRead; | 278 | CSecToRead secToRead; |
247 | SecToRead_CreateVTable(&secToRead); | 279 | SecToRead_CreateVTable(&secToRead); |
248 | secToRead.realStream = stream; | 280 | secToRead.realStream = stream; |
249 | |||
250 | RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt)) | 281 | RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt)) |
251 | return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; | 282 | return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; |
252 | } | 283 | } |
@@ -257,8 +288,7 @@ static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startO | |||
257 | 288 | ||
258 | void Xzs_Construct(CXzs *p) | 289 | void Xzs_Construct(CXzs *p) |
259 | { | 290 | { |
260 | p->num = p->numAllocated = 0; | 291 | Xzs_CONSTRUCT(p) |
261 | p->streams = 0; | ||
262 | } | 292 | } |
263 | 293 | ||
264 | void Xzs_Free(CXzs *p, ISzAllocPtr alloc) | 294 | void Xzs_Free(CXzs *p, ISzAllocPtr alloc) |
@@ -268,7 +298,7 @@ void Xzs_Free(CXzs *p, ISzAllocPtr alloc) | |||
268 | Xz_Free(&p->streams[i], alloc); | 298 | Xz_Free(&p->streams[i], alloc); |
269 | ISzAlloc_Free(alloc, p->streams); | 299 | ISzAlloc_Free(alloc, p->streams); |
270 | p->num = p->numAllocated = 0; | 300 | p->num = p->numAllocated = 0; |
271 | p->streams = 0; | 301 | p->streams = NULL; |
272 | } | 302 | } |
273 | 303 | ||
274 | UInt64 Xzs_GetNumBlocks(const CXzs *p) | 304 | UInt64 Xzs_GetNumBlocks(const CXzs *p) |
@@ -307,34 +337,49 @@ UInt64 Xzs_GetPackSize(const CXzs *p) | |||
307 | SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc) | 337 | SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc) |
308 | { | 338 | { |
309 | Int64 endOffset = 0; | 339 | Int64 endOffset = 0; |
340 | // it's supposed that CXzs object is empty here. | ||
341 | // if CXzs object is not empty, it will add new streams to that non-empty object. | ||
342 | // Xzs_Free(p, alloc); // it's optional call to empty CXzs object. | ||
310 | RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END)) | 343 | RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END)) |
311 | *startOffset = endOffset; | 344 | *startOffset = endOffset; |
312 | for (;;) | 345 | for (;;) |
313 | { | 346 | { |
314 | CXzStream st; | 347 | CXzStream st; |
315 | SRes res; | 348 | SRes res; |
316 | Xz_Construct(&st); | 349 | Xz_CONSTRUCT(&st) |
317 | res = Xz_ReadBackward(&st, stream, startOffset, alloc); | 350 | res = Xz_ReadBackward(&st, stream, startOffset, alloc); |
351 | // if (res == SZ_OK), then (*startOffset) is start offset of new stream if | ||
352 | // if (res != SZ_OK), then (*startOffset) is unchend or it's expected start offset of stream with error | ||
318 | st.startOffset = (UInt64)*startOffset; | 353 | st.startOffset = (UInt64)*startOffset; |
319 | RINOK(res) | 354 | // we must store (st) object to array, or we must free (st) local object. |
355 | if (res != SZ_OK) | ||
356 | { | ||
357 | Xz_Free(&st, alloc); | ||
358 | return res; | ||
359 | } | ||
320 | if (p->num == p->numAllocated) | 360 | if (p->num == p->numAllocated) |
321 | { | 361 | { |
322 | const size_t newNum = p->num + p->num / 4 + 1; | 362 | const size_t newNum = p->num + p->num / 4 + 1; |
323 | void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream)); | 363 | void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream)); |
324 | if (!data) | 364 | if (!data) |
365 | { | ||
366 | Xz_Free(&st, alloc); | ||
325 | return SZ_ERROR_MEM; | 367 | return SZ_ERROR_MEM; |
368 | } | ||
326 | p->numAllocated = newNum; | 369 | p->numAllocated = newNum; |
327 | if (p->num != 0) | 370 | if (p->num != 0) |
328 | memcpy(data, p->streams, p->num * sizeof(CXzStream)); | 371 | memcpy(data, p->streams, p->num * sizeof(CXzStream)); |
329 | ISzAlloc_Free(alloc, p->streams); | 372 | ISzAlloc_Free(alloc, p->streams); |
330 | p->streams = (CXzStream *)data; | 373 | p->streams = (CXzStream *)data; |
331 | } | 374 | } |
375 | // we use direct copying of raw data from local variable (st) to object in array. | ||
376 | // so we don't need to call Xz_Free(&st, alloc) after copying and after p->num++ | ||
332 | p->streams[p->num++] = st; | 377 | p->streams[p->num++] = st; |
333 | if (*startOffset == 0) | 378 | if (*startOffset == 0) |
334 | break; | 379 | return SZ_OK; |
335 | RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset)) | 380 | // seek operation is optional: |
381 | // RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset)) | ||
336 | if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK) | 382 | if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK) |
337 | return SZ_ERROR_PROGRESS; | 383 | return SZ_ERROR_PROGRESS; |
338 | } | 384 | } |
339 | return SZ_OK; | ||
340 | } | 385 | } |