2026-07-11
This week I found some bugs in several compression details, and I also found something I thought was a bug but isn't.
Most of these changes are mirrored in lDebug's mak.sh, either imported from lDebug to kernwrap or the other way around.
This is a test program for inicomp to test maximum block sizes with lots of uncompressible data. The data used was generated from the server's /dev/random device.
The lack of pointer normalisation is a major feature of the LZSA2 depacker, so it was undesirable to work around the problem on our end. Instead, I added the -S switch which makes the packer operate on 65_520 B blocks rather than full 64 KiB ones. Now regardless of the frame header, a normalised address at the start of a block (compressed or uncompressed) won't overflow until after the block is completely processed.
New commit added:
Add capital -S, smaller blocks (⇐ 65_520 rather than ⇐ 65_536)
This insures that whatever offset 0..15 is in SI after having just processed a frame header (either for a compressed or uncompressed block), adding the length of the source block won't overflow SI past 65_535. This invariant is required to efficiently handle large blocks without intra-block pointer normalisation. With large uncompressible (random data) blocks, lDOS inicomp could fail to depack as SI + length of block could overflow.
My comment on the lzsa Pull Request
ETA:
I commented on a 2024 SvarDOS bugz thread discussing the mvcomp format, as well as the three methods to determine "max bytes ahead" or the "allocation" required for depacking with destination starting below and overlapping the source. As previously discussed on the blog.
2. I also added a maxbytesahead calculation to the first depack run. The resulting maxunitsahead should match what the stubborn mvsize run calculates. That means if everything works we don't have to do the entire run any longer, just the first depack call would suffice.
I just updated my inicomp LZSA2, mvcomp, and LZMA-lzip depackers with a "scan" mode. This returns four results:
Success or error status Progress ticks (how often the counter variable is incremented) maxbytesahead Depacked sizeThe maxbytesahead calculation uses my technique from mvsize mentioned above. The scan caller can trivially calculate the maxparasahead (just divide by 16, rounding up) and then the allocation needed (maxparasahead plus compressed payload size plus init1 depacker size), which absent bugs exactly matches the stubborn binary search result that is found by repeatedly running the depacker.
Out of these methods, mvcomp and LZSA2 never refer to the depacked data except for window matches that generate more depacked data. Therefore, their scanners do not actually need any destination buffer memory and will work on virtual pointers. LZMA-lzip however, I found it does "peek" into previously depacked data to control the decompression, so its scanner has to actually depack the data.
Here's an example:
~/proj/testrand$ INICOMP_WINNER=smallest INICOMP_METHOD='lzsa2 mvcomp lzd' use_build_decomp_test_size=1 use_build_decomp_scan=1 use_build_decomp_test_file=1 use_build_decomp_test_xms=1 ./mak.sh -DEMPTYThis uses the testrand, kernwrap, lmacros, ldosboot, inicomp, and scanptab repos from our hgweb
And here's the parts of the example output that are interesting:
About to Execute : ttestr.com Note: Scan depack=48 pack=49 ahead=4,para=1 alloc=84 tick=2 Info: 1SSSSSSF 84 About to Execute : ttestr.com c Note: Scan depack=48 pack=49 ahead=4,para=1 alloc=84 tick=2 0 0 progress dots, method lzsa2 ... About to Execute : ttestr.com Note: Scan depack=48 pack=38 ahead=10,para=1 alloc=59 tick=4 Info: 1SSSSSSF 59 About to Execute : ttestr.com c Note: Scan depack=48 pack=38 ahead=10,para=1 alloc=59 tick=4 0 0 progress dots, method mvcomp ... About to Execute : ttestr.com Note: Scan depack=48 pack=74 ahead=14688,para=918 alloc=1099 tick=36 Info: 1SFFSFFFSSSF 1099 About to Execute : ttestr.com c Note: Scan depack=48 pack=74 ahead=14688,para=918 alloc=1099 tick=36 0 0 progress dots, method lzd(LZMA-lzip has several KiB of local variables that it puts into the depack buffer, hence the large maxbytesahead.)
Note how the
alloc=exactly matches the number displayed after theInfo: 1…line.