| 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; |
| 3 | const mem = std.mem; |
| 4 | const Allocator = std.mem.Allocator; |
| 5 | const assert = std.debug.assert; |
| 6 | const ArrayList = std.ArrayList; |
| 7 | const Writer = std.Io.Writer; |
| 8 | const Reader = std.Io.Reader; |
| 9 | |
| 10 | pub const RangeDecoder = struct { |
| 11 | range: u32, |
| 12 | code: u32, |
| 13 | |
| 14 | pub fn init(reader: *Reader) !RangeDecoder { |
| 15 | var counter: u64 = 0; |
| 16 | return initCounting(reader, &counter); |
| 17 | } |
| 18 | |
| 19 | pub fn initCounting(reader: *Reader, n_read: *u64) !RangeDecoder { |
| 20 | const reserved = try reader.takeByte(); |
| 21 | n_read.* += 1; |
| 22 | if (reserved != 0) return error.InvalidRangeCode; |
| 23 | const code = try reader.takeInt(u32, .big); |
| 24 | n_read.* += 4; |
| 25 | return .{ |
| 26 | .range = 0xFFFF_FFFF, |
| 27 | .code = code, |
| 28 | }; |
| 29 | } |
| 30 | |
| 31 | pub fn isFinished(self: RangeDecoder) bool { |
| 32 | return self.code == 0; |
| 33 | } |
| 34 | |
| 35 | fn normalize(self: *RangeDecoder, reader: *Reader, n_read: *u64) !void { |
| 36 | if (self.range < 0x0100_0000) { |
| 37 | self.range <<= 8; |
| 38 | self.code = (self.code << 8) ^ @as(u32, try reader.takeByte()); |
| 39 | n_read.* += 1; |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | fn getBit(self: *RangeDecoder, reader: *Reader, n_read: *u64) !bool { |
| 44 | self.range >>= 1; |
| 45 | |
| 46 | const bit = self.code >= self.range; |
| 47 | if (bit) self.code -= self.range; |
| 48 | |
| 49 | try self.normalize(reader, n_read); |
| 50 | return bit; |
| 51 | } |
| 52 | |
| 53 | pub fn get(self: *RangeDecoder, reader: *Reader, count: usize, n_read: *u64) !u32 { |
| 54 | var result: u32 = 0; |
| 55 | for (0..count) |_| { |
| 56 | result = (result << 1) ^ @intFromBool(try self.getBit(reader, n_read)); |
| 57 | } |
| 58 | return result; |
| 59 | } |
| 60 | |
| 61 | pub fn decodeBit(self: *RangeDecoder, reader: *Reader, prob: *u16, n_read: *u64) !bool { |
| 62 | const bound = (self.range >> 11) * prob.*; |
| 63 | |
| 64 | if (self.code < bound) { |
| 65 | prob.* += (0x800 - prob.*) >> 5; |
| 66 | self.range = bound; |
| 67 | |
| 68 | try self.normalize(reader, n_read); |
| 69 | return false; |
| 70 | } else { |
| 71 | prob.* -= prob.* >> 5; |
| 72 | self.code -= bound; |
| 73 | self.range -= bound; |
| 74 | |
| 75 | try self.normalize(reader, n_read); |
| 76 | return true; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | fn parseBitTree( |
| 81 | self: *RangeDecoder, |
| 82 | reader: *Reader, |
| 83 | num_bits: u5, |
| 84 | probs: []u16, |
| 85 | n_read: *u64, |
| 86 | ) !u32 { |
| 87 | var tmp: u32 = 1; |
| 88 | var i: @TypeOf(num_bits) = 0; |
| 89 | while (i < num_bits) : (i += 1) { |
| 90 | const bit = try self.decodeBit(reader, &probs[tmp], n_read); |
| 91 | tmp = (tmp << 1) ^ @intFromBool(bit); |
| 92 | } |
| 93 | return tmp - (@as(u32, 1) << num_bits); |
| 94 | } |
| 95 | |
| 96 | pub fn parseReverseBitTree( |
| 97 | self: *RangeDecoder, |
| 98 | reader: *Reader, |
| 99 | num_bits: u5, |
| 100 | probs: []u16, |
| 101 | offset: usize, |
| 102 | n_read: *u64, |
| 103 | ) !u32 { |
| 104 | var result: u32 = 0; |
| 105 | var tmp: usize = 1; |
| 106 | var i: @TypeOf(num_bits) = 0; |
| 107 | while (i < num_bits) : (i += 1) { |
| 108 | const bit = @intFromBool(try self.decodeBit(reader, &probs[offset + tmp], n_read)); |
| 109 | tmp = (tmp << 1) ^ bit; |
| 110 | result ^= @as(u32, bit) << i; |
| 111 | } |
| 112 | return result; |
| 113 | } |
| 114 | }; |
| 115 | |
| 116 | pub const Decode = struct { |
| 117 | properties: Properties, |
| 118 | literal_probs: Vec2d, |
| 119 | pos_slot_decoder: [4]BitTree(6), |
| 120 | align_decoder: BitTree(4), |
| 121 | pos_decoders: [115]u16, |
| 122 | is_match: [192]u16, |
| 123 | is_rep: [12]u16, |
| 124 | is_rep_g0: [12]u16, |
| 125 | is_rep_g1: [12]u16, |
| 126 | is_rep_g2: [12]u16, |
| 127 | is_rep_0long: [192]u16, |
| 128 | state: usize, |
| 129 | rep: [4]usize, |
| 130 | len_decoder: LenDecoder, |
| 131 | rep_len_decoder: LenDecoder, |
| 132 | |
| 133 | pub fn init(gpa: Allocator, properties: Properties) !Decode { |
| 134 | return .{ |
| 135 | .properties = properties, |
| 136 | .literal_probs = try Vec2d.init(gpa, 0x400, @as(usize, 1) << (properties.lc + properties.lp), 0x300), |
| 137 | .pos_slot_decoder = @splat(.{}), |
| 138 | .align_decoder = .{}, |
| 139 | .pos_decoders = @splat(0x400), |
| 140 | .is_match = @splat(0x400), |
| 141 | .is_rep = @splat(0x400), |
| 142 | .is_rep_g0 = @splat(0x400), |
| 143 | .is_rep_g1 = @splat(0x400), |
| 144 | .is_rep_g2 = @splat(0x400), |
| 145 | .is_rep_0long = @splat(0x400), |
| 146 | .state = 0, |
| 147 | .rep = @splat(0), |
| 148 | .len_decoder = .{}, |
| 149 | .rep_len_decoder = .{}, |
| 150 | }; |
| 151 | } |
| 152 | |
| 153 | pub fn deinit(self: *Decode, gpa: Allocator) void { |
| 154 | self.literal_probs.deinit(gpa); |
| 155 | self.* = undefined; |
| 156 | } |
| 157 | |
| 158 | pub fn resetState(self: *Decode, gpa: Allocator, new_props: Properties) !void { |
| 159 | new_props.validate(); |
| 160 | if (self.properties.lc + self.properties.lp == new_props.lc + new_props.lp) { |
| 161 | self.literal_probs.fill(0x400); |
| 162 | } else { |
| 163 | self.literal_probs.deinit(gpa); |
| 164 | self.literal_probs = try Vec2d.init(gpa, 0x400, @as(usize, 1) << (new_props.lc + new_props.lp), 0x300); |
| 165 | } |
| 166 | |
| 167 | self.properties = new_props; |
| 168 | for (&self.pos_slot_decoder) |*t| t.reset(); |
| 169 | self.align_decoder.reset(); |
| 170 | self.pos_decoders = @splat(0x400); |
| 171 | self.is_match = @splat(0x400); |
| 172 | self.is_rep = @splat(0x400); |
| 173 | self.is_rep_g0 = @splat(0x400); |
| 174 | self.is_rep_g1 = @splat(0x400); |
| 175 | self.is_rep_g2 = @splat(0x400); |
| 176 | self.is_rep_0long = @splat(0x400); |
| 177 | self.state = 0; |
| 178 | self.rep = @splat(0); |
| 179 | self.len_decoder.reset(); |
| 180 | self.rep_len_decoder.reset(); |
| 181 | } |
| 182 | |
| 183 | pub fn process( |
| 184 | self: *Decode, |
| 185 | reader: *Reader, |
| 186 | allocating: *Writer.Allocating, |
| 187 | /// `CircularBuffer` or `std.compress.lzma2.AccumBuffer`. |
| 188 | buffer: anytype, |
| 189 | decoder: *RangeDecoder, |
| 190 | n_read: *u64, |
| 191 | ) !ProcessingStatus { |
| 192 | const gpa = allocating.allocator; |
| 193 | const writer = &allocating.writer; |
| 194 | const pos_state = buffer.len & ((@as(usize, 1) << self.properties.pb) - 1); |
| 195 | |
| 196 | if (!try decoder.decodeBit(reader, &self.is_match[(self.state << 4) + pos_state], n_read)) { |
| 197 | const byte: u8 = try self.decodeLiteral(reader, buffer, decoder, n_read); |
| 198 | |
| 199 | try buffer.appendLiteral(gpa, byte, writer); |
| 200 | |
| 201 | self.state = if (self.state < 4) |
| 202 | 0 |
| 203 | else if (self.state < 10) |
| 204 | self.state - 3 |
| 205 | else |
| 206 | self.state - 6; |
| 207 | return .more; |
| 208 | } |
| 209 | |
| 210 | var len: usize = undefined; |
| 211 | if (try decoder.decodeBit(reader, &self.is_rep[self.state], n_read)) { |
| 212 | if (!try decoder.decodeBit(reader, &self.is_rep_g0[self.state], n_read)) { |
| 213 | if (!try decoder.decodeBit(reader, &self.is_rep_0long[(self.state << 4) + pos_state], n_read)) { |
| 214 | self.state = if (self.state < 7) 9 else 11; |
| 215 | const dist = self.rep[0] + 1; |
| 216 | try buffer.appendLz(gpa, 1, dist, writer); |
| 217 | return .more; |
| 218 | } |
| 219 | } else { |
| 220 | const idx: usize = if (!try decoder.decodeBit(reader, &self.is_rep_g1[self.state], n_read)) |
| 221 | 1 |
| 222 | else if (!try decoder.decodeBit(reader, &self.is_rep_g2[self.state], n_read)) |
| 223 | 2 |
| 224 | else |
| 225 | 3; |
| 226 | const dist = self.rep[idx]; |
| 227 | var i = idx; |
| 228 | while (i > 0) : (i -= 1) { |
| 229 | self.rep[i] = self.rep[i - 1]; |
| 230 | } |
| 231 | self.rep[0] = dist; |
| 232 | } |
| 233 | |
| 234 | len = try self.rep_len_decoder.decode(reader, decoder, pos_state, n_read); |
| 235 | |
| 236 | self.state = if (self.state < 7) 8 else 11; |
| 237 | } else { |
| 238 | self.rep[3] = self.rep[2]; |
| 239 | self.rep[2] = self.rep[1]; |
| 240 | self.rep[1] = self.rep[0]; |
| 241 | |
| 242 | len = try self.len_decoder.decode(reader, decoder, pos_state, n_read); |
| 243 | |
| 244 | self.state = if (self.state < 7) 7 else 10; |
| 245 | |
| 246 | const rep_0 = try self.decodeDistance(reader, decoder, len, n_read); |
| 247 | |
| 248 | self.rep[0] = rep_0; |
| 249 | if (self.rep[0] == 0xFFFF_FFFF) { |
| 250 | if (decoder.isFinished()) { |
| 251 | return .finished; |
| 252 | } |
| 253 | return error.CorruptInput; |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | len += 2; |
| 258 | |
| 259 | const dist = self.rep[0] + 1; |
| 260 | try buffer.appendLz(gpa, len, dist, writer); |
| 261 | |
| 262 | return .more; |
| 263 | } |
| 264 | |
| 265 | fn decodeLiteral( |
| 266 | self: *Decode, |
| 267 | reader: *Reader, |
| 268 | /// `CircularBuffer` or `std.compress.lzma2.AccumBuffer`. |
| 269 | buffer: anytype, |
| 270 | decoder: *RangeDecoder, |
| 271 | n_read: *u64, |
| 272 | ) !u8 { |
| 273 | const def_prev_byte = 0; |
| 274 | const prev_byte = @as(usize, buffer.lastOr(def_prev_byte)); |
| 275 | |
| 276 | var result: usize = 1; |
| 277 | const lit_state = ((buffer.len & ((@as(usize, 1) << self.properties.lp) - 1)) << self.properties.lc) + |
| 278 | (prev_byte >> (8 - self.properties.lc)); |
| 279 | const probs = try self.literal_probs.get(lit_state); |
| 280 | |
| 281 | if (self.state >= 7) { |
| 282 | var match_byte = @as(usize, try buffer.lastN(self.rep[0] + 1)); |
| 283 | |
| 284 | while (result < 0x100) { |
| 285 | const match_bit = (match_byte >> 7) & 1; |
| 286 | match_byte <<= 1; |
| 287 | const bit = @intFromBool(try decoder.decodeBit( |
| 288 | reader, |
| 289 | &probs[((@as(usize, 1) + match_bit) << 8) + result], |
| 290 | n_read, |
| 291 | )); |
| 292 | result = (result << 1) ^ bit; |
| 293 | if (match_bit != bit) { |
| 294 | break; |
| 295 | } |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | while (result < 0x100) { |
| 300 | result = (result << 1) ^ @intFromBool(try decoder.decodeBit(reader, &probs[result], n_read)); |
| 301 | } |
| 302 | |
| 303 | return @truncate(result - 0x100); |
| 304 | } |
| 305 | |
| 306 | fn decodeDistance( |
| 307 | self: *Decode, |
| 308 | reader: *Reader, |
| 309 | decoder: *RangeDecoder, |
| 310 | length: usize, |
| 311 | n_read: *u64, |
| 312 | ) !usize { |
| 313 | const len_state = if (length > 3) 3 else length; |
| 314 | |
| 315 | const pos_slot: usize = try self.pos_slot_decoder[len_state].parse(reader, decoder, n_read); |
| 316 | if (pos_slot < 4) return pos_slot; |
| 317 | |
| 318 | const num_direct_bits = @as(u5, @intCast((pos_slot >> 1) - 1)); |
| 319 | var result = (2 ^ (pos_slot & 1)) << num_direct_bits; |
| 320 | |
| 321 | if (pos_slot < 14) { |
| 322 | result += try decoder.parseReverseBitTree( |
| 323 | reader, |
| 324 | num_direct_bits, |
| 325 | &self.pos_decoders, |
| 326 | result - pos_slot, |
| 327 | n_read, |
| 328 | ); |
| 329 | } else { |
| 330 | result += @as(usize, try decoder.get(reader, num_direct_bits - 4, n_read)) << 4; |
| 331 | result += try self.align_decoder.parseReverse(reader, decoder, n_read); |
| 332 | } |
| 333 | |
| 334 | return result; |
| 335 | } |
| 336 | |
| 337 | /// A circular buffer for LZ sequences |
| 338 | pub const CircularBuffer = struct { |
| 339 | /// Circular buffer |
| 340 | buf: ArrayList(u8), |
| 341 | /// Length of the buffer |
| 342 | dict_size: usize, |
| 343 | /// Buffer memory limit |
| 344 | mem_limit: usize, |
| 345 | /// Current position |
| 346 | cursor: usize, |
| 347 | /// Total number of bytes sent through the buffer |
| 348 | len: usize, |
| 349 | |
| 350 | pub fn init(dict_size: usize, mem_limit: usize) CircularBuffer { |
| 351 | return .{ |
| 352 | .buf = .empty, |
| 353 | .dict_size = dict_size, |
| 354 | .mem_limit = mem_limit, |
| 355 | .cursor = 0, |
| 356 | .len = 0, |
| 357 | }; |
| 358 | } |
| 359 | |
| 360 | pub fn get(self: CircularBuffer, index: usize) u8 { |
| 361 | return if (0 <= index and index < self.buf.items.len) self.buf.items[index] else 0; |
| 362 | } |
| 363 | |
| 364 | pub fn set(self: *CircularBuffer, gpa: Allocator, index: usize, value: u8) !void { |
| 365 | if (index >= self.mem_limit) { |
| 366 | return error.CorruptInput; |
| 367 | } |
| 368 | try self.buf.ensureTotalCapacity(gpa, index + 1); |
| 369 | while (self.buf.items.len < index) { |
| 370 | self.buf.appendAssumeCapacity(0); |
| 371 | } |
| 372 | self.buf.appendAssumeCapacity(value); |
| 373 | } |
| 374 | |
| 375 | /// Retrieve the last byte or return a default |
| 376 | pub fn lastOr(self: CircularBuffer, lit: u8) u8 { |
| 377 | return if (self.len == 0) |
| 378 | lit |
| 379 | else |
| 380 | self.get((self.dict_size + self.cursor - 1) % self.dict_size); |
| 381 | } |
| 382 | |
| 383 | /// Retrieve the n-th last byte |
| 384 | pub fn lastN(self: CircularBuffer, dist: usize) !u8 { |
| 385 | if (dist > self.dict_size or dist > self.len) { |
| 386 | return error.CorruptInput; |
| 387 | } |
| 388 | |
| 389 | const offset = (self.dict_size + self.cursor - dist) % self.dict_size; |
| 390 | return self.get(offset); |
| 391 | } |
| 392 | |
| 393 | /// Append a literal |
| 394 | pub fn appendLiteral( |
| 395 | self: *CircularBuffer, |
| 396 | gpa: Allocator, |
| 397 | lit: u8, |
| 398 | writer: *Writer, |
| 399 | ) !void { |
| 400 | try self.set(gpa, self.cursor, lit); |
| 401 | self.cursor += 1; |
| 402 | self.len += 1; |
| 403 | |
| 404 | // Flush the circular buffer to the output |
| 405 | if (self.cursor == self.dict_size) { |
| 406 | try writer.writeAll(self.buf.items); |
| 407 | self.cursor = 0; |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | /// Fetch an LZ sequence (length, distance) from inside the buffer |
| 412 | pub fn appendLz( |
| 413 | self: *CircularBuffer, |
| 414 | gpa: Allocator, |
| 415 | len: usize, |
| 416 | dist: usize, |
| 417 | writer: *Writer, |
| 418 | ) !void { |
| 419 | if (dist > self.dict_size or dist > self.len) { |
| 420 | return error.CorruptInput; |
| 421 | } |
| 422 | |
| 423 | var offset = (self.dict_size + self.cursor - dist) % self.dict_size; |
| 424 | var i: usize = 0; |
| 425 | while (i < len) : (i += 1) { |
| 426 | const x = self.get(offset); |
| 427 | try self.appendLiteral(gpa, x, writer); |
| 428 | offset += 1; |
| 429 | if (offset == self.dict_size) { |
| 430 | offset = 0; |
| 431 | } |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | pub fn finish(self: *CircularBuffer, writer: *Writer) !void { |
| 436 | if (self.cursor > 0) { |
| 437 | try writer.writeAll(self.buf.items[0..self.cursor]); |
| 438 | self.cursor = 0; |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | pub fn deinit(self: *CircularBuffer, gpa: Allocator) void { |
| 443 | self.buf.deinit(gpa); |
| 444 | self.* = undefined; |
| 445 | } |
| 446 | }; |
| 447 | |
| 448 | pub fn BitTree(comptime num_bits: usize) type { |
| 449 | return struct { |
| 450 | probs: [1 << num_bits]u16 = @splat(0x400), |
| 451 | |
| 452 | pub fn parse(self: *@This(), reader: *Reader, decoder: *RangeDecoder, n_read: *u64) !u32 { |
| 453 | return decoder.parseBitTree(reader, num_bits, &self.probs, n_read); |
| 454 | } |
| 455 | |
| 456 | pub fn parseReverse( |
| 457 | self: *@This(), |
| 458 | reader: *Reader, |
| 459 | decoder: *RangeDecoder, |
| 460 | n_read: *u64, |
| 461 | ) !u32 { |
| 462 | return decoder.parseReverseBitTree(reader, num_bits, &self.probs, 0, n_read); |
| 463 | } |
| 464 | |
| 465 | pub fn reset(self: *@This()) void { |
| 466 | @memset(&self.probs, 0x400); |
| 467 | } |
| 468 | }; |
| 469 | } |
| 470 | |
| 471 | pub const LenDecoder = struct { |
| 472 | choice: u16 = 0x400, |
| 473 | choice2: u16 = 0x400, |
| 474 | low_coder: [16]BitTree(3) = @splat(.{}), |
| 475 | mid_coder: [16]BitTree(3) = @splat(.{}), |
| 476 | high_coder: BitTree(8) = .{}, |
| 477 | |
| 478 | pub fn decode( |
| 479 | self: *LenDecoder, |
| 480 | reader: *Reader, |
| 481 | decoder: *RangeDecoder, |
| 482 | pos_state: usize, |
| 483 | n_read: *u64, |
| 484 | ) !usize { |
| 485 | if (!try decoder.decodeBit(reader, &self.choice, n_read)) { |
| 486 | return @as(usize, try self.low_coder[pos_state].parse(reader, decoder, n_read)); |
| 487 | } else if (!try decoder.decodeBit(reader, &self.choice2, n_read)) { |
| 488 | return @as(usize, try self.mid_coder[pos_state].parse(reader, decoder, n_read)) + 8; |
| 489 | } else { |
| 490 | return @as(usize, try self.high_coder.parse(reader, decoder, n_read)) + 16; |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | pub fn reset(self: *LenDecoder) void { |
| 495 | self.choice = 0x400; |
| 496 | self.choice2 = 0x400; |
| 497 | for (&self.low_coder) |*t| t.reset(); |
| 498 | for (&self.mid_coder) |*t| t.reset(); |
| 499 | self.high_coder.reset(); |
| 500 | } |
| 501 | }; |
| 502 | |
| 503 | pub const Vec2d = struct { |
| 504 | data: []u16, |
| 505 | cols: usize, |
| 506 | |
| 507 | pub fn init(gpa: Allocator, value: u16, w: usize, h: usize) !Vec2d { |
| 508 | const len = try math.mul(usize, w, h); |
| 509 | const data = try gpa.alloc(u16, len); |
| 510 | @memset(data, value); |
| 511 | return .{ |
| 512 | .data = data, |
| 513 | .cols = h, |
| 514 | }; |
| 515 | } |
| 516 | |
| 517 | pub fn deinit(v: *Vec2d, gpa: Allocator) void { |
| 518 | gpa.free(v.data); |
| 519 | v.* = undefined; |
| 520 | } |
| 521 | |
| 522 | pub fn fill(v: *Vec2d, value: u16) void { |
| 523 | @memset(v.data, value); |
| 524 | } |
| 525 | |
| 526 | fn get(v: Vec2d, row: usize) ![]u16 { |
| 527 | const start_row = try math.mul(usize, row, v.cols); |
| 528 | const end_row = try math.add(usize, start_row, v.cols); |
| 529 | return v.data[start_row..end_row]; |
| 530 | } |
| 531 | }; |
| 532 | |
| 533 | pub const Options = struct { |
| 534 | unpacked_size: UnpackedSize = .read_from_header, |
| 535 | mem_limit: ?usize = null, |
| 536 | allow_incomplete: bool = false, |
| 537 | }; |
| 538 | |
| 539 | pub const UnpackedSize = union(enum) { |
| 540 | read_from_header, |
| 541 | read_header_but_use_provided: ?u64, |
| 542 | use_provided: ?u64, |
| 543 | }; |
| 544 | |
| 545 | const ProcessingStatus = enum { |
| 546 | more, |
| 547 | finished, |
| 548 | }; |
| 549 | |
| 550 | pub const Properties = struct { |
| 551 | lc: u4, |
| 552 | lp: u3, |
| 553 | pb: u3, |
| 554 | |
| 555 | fn validate(self: Properties) void { |
| 556 | assert(self.lc <= 8); |
| 557 | assert(self.lp <= 4); |
| 558 | assert(self.pb <= 4); |
| 559 | } |
| 560 | }; |
| 561 | |
| 562 | pub const Params = struct { |
| 563 | properties: Properties, |
| 564 | dict_size: u32, |
| 565 | unpacked_size: ?u64, |
| 566 | |
| 567 | pub fn readHeader(reader: *Reader, options: Options) !Params { |
| 568 | var props = try reader.takeByte(); |
| 569 | if (props >= 225) return error.CorruptInput; |
| 570 | |
| 571 | const lc: u4 = @intCast(props % 9); |
| 572 | props /= 9; |
| 573 | const lp: u3 = @intCast(props % 5); |
| 574 | props /= 5; |
| 575 | const pb: u3 = @intCast(props); |
| 576 | |
| 577 | const dict_size_provided = try reader.takeInt(u32, .little); |
| 578 | const dict_size = @max(0x1000, dict_size_provided); |
| 579 | |
| 580 | const unpacked_size = switch (options.unpacked_size) { |
| 581 | .read_from_header => blk: { |
| 582 | const unpacked_size_provided = try reader.takeInt(u64, .little); |
| 583 | const marker_mandatory = unpacked_size_provided == 0xFFFF_FFFF_FFFF_FFFF; |
| 584 | break :blk if (marker_mandatory) null else unpacked_size_provided; |
| 585 | }, |
| 586 | .read_header_but_use_provided => |x| blk: { |
| 587 | _ = try reader.takeInt(u64, .little); |
| 588 | break :blk x; |
| 589 | }, |
| 590 | .use_provided => |x| x, |
| 591 | }; |
| 592 | |
| 593 | return .{ |
| 594 | .properties = .{ .lc = lc, .lp = lp, .pb = pb }, |
| 595 | .dict_size = dict_size, |
| 596 | .unpacked_size = unpacked_size, |
| 597 | }; |
| 598 | } |
| 599 | }; |
| 600 | }; |
| 601 | |
| 602 | pub const Decompress = struct { |
| 603 | gpa: Allocator, |
| 604 | input: *Reader, |
| 605 | reader: Reader, |
| 606 | buffer: Decode.CircularBuffer, |
| 607 | range_decoder: RangeDecoder, |
| 608 | decode: Decode, |
| 609 | err: ?Error, |
| 610 | unpacked_size: ?u64, |
| 611 | |
| 612 | pub const Error = error{ |
| 613 | OutOfMemory, |
| 614 | ReadFailed, |
| 615 | CorruptInput, |
| 616 | DecompressedSizeMismatch, |
| 617 | EndOfStream, |
| 618 | Overflow, |
| 619 | }; |
| 620 | |
| 621 | /// Takes ownership of `buffer` which may be resized with `gpa`. |
| 622 | /// |
| 623 | /// LZMA was explicitly designed to take advantage of large heap memory |
| 624 | /// being available, with a dictionary size anywhere from 4K to 4G. Thus, |
| 625 | /// this API dynamically allocates the dictionary as-needed. |
| 626 | pub fn initParams( |
| 627 | input: *Reader, |
| 628 | gpa: Allocator, |
| 629 | buffer: []u8, |
| 630 | params: Decode.Params, |
| 631 | mem_limit: usize, |
| 632 | ) !Decompress { |
| 633 | return .{ |
| 634 | .gpa = gpa, |
| 635 | .input = input, |
| 636 | .buffer = Decode.CircularBuffer.init(params.dict_size, mem_limit), |
| 637 | .range_decoder = try RangeDecoder.init(input), |
| 638 | .decode = try Decode.init(gpa, params.properties), |
| 639 | .reader = .{ |
| 640 | .buffer = buffer, |
| 641 | .vtable = &.{ |
| 642 | .readVec = readVec, |
| 643 | .stream = stream, |
| 644 | .discard = discard, |
| 645 | }, |
| 646 | .seek = 0, |
| 647 | .end = 0, |
| 648 | }, |
| 649 | .err = null, |
| 650 | .unpacked_size = params.unpacked_size, |
| 651 | }; |
| 652 | } |
| 653 | |
| 654 | /// Takes ownership of `buffer` which may be resized with `gpa`. |
| 655 | /// |
| 656 | /// LZMA was explicitly designed to take advantage of large heap memory |
| 657 | /// being available, with a dictionary size anywhere from 4K to 4G. Thus, |
| 658 | /// this API dynamically allocates the dictionary as-needed. |
| 659 | pub fn initOptions( |
| 660 | input: *Reader, |
| 661 | gpa: Allocator, |
| 662 | buffer: []u8, |
| 663 | options: Decode.Options, |
| 664 | mem_limit: usize, |
| 665 | ) !Decompress { |
| 666 | const params = try Decode.Params.readHeader(input, options); |
| 667 | return initParams(input, gpa, buffer, params, mem_limit); |
| 668 | } |
| 669 | |
| 670 | /// Reclaim ownership of the buffer passed to `init`. |
| 671 | pub fn takeBuffer(d: *Decompress) []u8 { |
| 672 | const buffer = d.reader.buffer; |
| 673 | d.reader.buffer = &.{}; |
| 674 | return buffer; |
| 675 | } |
| 676 | |
| 677 | pub fn deinit(d: *Decompress) void { |
| 678 | const gpa = d.gpa; |
| 679 | gpa.free(d.reader.buffer); |
| 680 | d.buffer.deinit(gpa); |
| 681 | d.decode.deinit(gpa); |
| 682 | d.* = undefined; |
| 683 | } |
| 684 | |
| 685 | fn readVec(r: *Reader, data: [][]u8) Reader.Error!usize { |
| 686 | _ = data; |
| 687 | return readIndirect(r); |
| 688 | } |
| 689 | |
| 690 | fn stream(r: *Reader, w: *Writer, limit: std.Io.Limit) Reader.StreamError!usize { |
| 691 | _ = w; |
| 692 | _ = limit; |
| 693 | return readIndirect(r); |
| 694 | } |
| 695 | |
| 696 | fn discard(r: *Reader, limit: std.Io.Limit) Reader.Error!usize { |
| 697 | const d: *Decompress = @alignCast(@fieldParentPtr("reader", r)); |
| 698 | _ = d; |
| 699 | _ = limit; |
| 700 | @panic("TODO"); |
| 701 | } |
| 702 | |
| 703 | fn readIndirect(r: *Reader) Reader.Error!usize { |
| 704 | const d: *Decompress = @alignCast(@fieldParentPtr("reader", r)); |
| 705 | const gpa = d.gpa; |
| 706 | var allocating = Writer.Allocating.initOwnedSlice(gpa, r.buffer); |
| 707 | allocating.writer.end = r.end; |
| 708 | defer { |
| 709 | r.buffer = allocating.writer.buffer; |
| 710 | r.end = allocating.writer.end; |
| 711 | } |
| 712 | if (d.decode.state == math.maxInt(usize)) return error.EndOfStream; |
| 713 | |
| 714 | process_next: { |
| 715 | if (d.unpacked_size) |unpacked_size| { |
| 716 | if (d.buffer.len >= unpacked_size) break :process_next; |
| 717 | } else if (d.range_decoder.isFinished()) { |
| 718 | break :process_next; |
| 719 | } |
| 720 | var n_read: u64 = 0; |
| 721 | switch (d.decode.process(d.input, &allocating, &d.buffer, &d.range_decoder, &n_read) catch |err| switch (err) { |
| 722 | error.WriteFailed => { |
| 723 | d.err = error.OutOfMemory; |
| 724 | return error.ReadFailed; |
| 725 | }, |
| 726 | error.EndOfStream => { |
| 727 | d.err = error.EndOfStream; |
| 728 | return error.ReadFailed; |
| 729 | }, |
| 730 | else => |e| { |
| 731 | d.err = e; |
| 732 | return error.ReadFailed; |
| 733 | }, |
| 734 | }) { |
| 735 | .more => return 0, |
| 736 | .finished => break :process_next, |
| 737 | } |
| 738 | } |
| 739 | |
| 740 | if (d.unpacked_size) |unpacked_size| { |
| 741 | if (d.buffer.len != unpacked_size) { |
| 742 | d.err = error.DecompressedSizeMismatch; |
| 743 | return error.ReadFailed; |
| 744 | } |
| 745 | } |
| 746 | |
| 747 | d.buffer.finish(&allocating.writer) catch |err| switch (err) { |
| 748 | error.WriteFailed => { |
| 749 | d.err = error.OutOfMemory; |
| 750 | return error.ReadFailed; |
| 751 | }, |
| 752 | }; |
| 753 | d.decode.state = math.maxInt(usize); |
| 754 | return 0; |
| 755 | } |
| 756 | }; |
| 757 | |
| 758 | test { |
| 759 | _ = @import("lzma/test.zig"); |
| 760 | } |