public static Int64 FetchBytes(ref ChmFileInfo h, ref byte[] buf, UInt64 os, Int64 len) { Int64 readLen = 0; long cur = 0; if (h.fd == null) { return(readLen); } h.mutex.WaitOne(); cur = h.fd.Seek((long)os, SeekOrigin.Begin); readLen = h.fd.Read(buf, 0, (int)len); h.mutex.ReleaseMutex(); return(readLen); }
private ChmFile(string filename) { _h = new ChmFileInfo(); _filename = filename; }
/* decompress the block. must have lzx_mutex. */ public static Int64 DecompressBlock(ref ChmFileInfo h, UInt64 block, ref byte[] ubuffer) { byte[] cbuffer = new byte[h.reset_table.block_len + 6144]; long cbufferpos = 0; UInt64 cmpStart = 0; /* compressed start */ Int64 cmpLen = 0; /* compressed len */ int indexSlot; /* cache index slot */ uint lbuffer; /* local buffer ptr */ UInt32 blockAlign = (UInt32)(block % h.reset_blkcount); /* reset intvl. aln. */ UInt32 i; /* local loop index */ /* let the caching system pull its weight! */ if ((int)(block - blockAlign) <= h.lzx_last_block && (int)block >= h.lzx_last_block) { blockAlign = (uint)((int)block - h.lzx_last_block); } /* check if we need previous blocks */ if (blockAlign != 0) { /* fetch all required previous blocks since last reset */ for (i = blockAlign; i > 0; i--) { UInt32 curBlockIdx = (UInt32)(block - (LONGUINT64)i); /* check if we most recently decompressed the previous block */ if (h.lzx_last_block != curBlockIdx) { if ((curBlockIdx % h.reset_blkcount) == 0) { Lzx.LZXreset(h.lzx_state); } indexSlot = (int)((curBlockIdx) % h.cache_num_blocks); if (h.cache_blocks[indexSlot] == null) { h.cache_blocks[indexSlot] = new byte[h.reset_table.block_len]; } h.cache_block_indices[indexSlot] = curBlockIdx; lbuffer = (uint)indexSlot; /* decompress the previous block */ if (!GetCmpBlockBounds(ref h, curBlockIdx, ref cmpStart, ref cmpLen) || cmpLen < 0 || cmpLen > (LONGINT64)h.reset_table.block_len + 6144 || Storage.FetchBytes(ref h, ref cbuffer, cmpStart, cmpLen) != cmpLen || Lzx.LZXdecompress(h.lzx_state, ref cbuffer, cbufferpos, ref h.cache_blocks[lbuffer], 0, (int)cmpLen, (int)h.reset_table.block_len) != Lzx.DECR_OK) { return((Int64)0); } h.lzx_last_block = (int)curBlockIdx; } } } else { if ((block % h.reset_blkcount) == 0) { Lzx.LZXreset(h.lzx_state); } } /* allocate slot in cache */ indexSlot = (int)(block % (LONGUINT64)h.cache_num_blocks); if (h.cache_blocks[indexSlot] == null) { h.cache_blocks[indexSlot] = new byte[h.reset_table.block_len]; } h.cache_block_indices[indexSlot] = block; lbuffer = (uint)indexSlot; ubuffer = h.cache_blocks[lbuffer]; /* decompress the block we actually want */ if (!GetCmpBlockBounds(ref h, block, ref cmpStart, ref cmpLen) || Storage.FetchBytes(ref h, ref cbuffer, cmpStart, cmpLen) != cmpLen || Lzx.LZXdecompress(h.lzx_state, ref cbuffer, cbufferpos, ref h.cache_blocks[lbuffer], 0, (int)cmpLen, (int)h.reset_table.block_len) != Lzx.DECR_OK) { return((Int64)0); } h.lzx_last_block = (int)block; /* XXX: modify LZX routines to return the length of the data they * decompressed and return that instead, for an extra sanity check. */ return((Int64)h.reset_table.block_len); }
/* get the bounds of a compressed block. return 0 on failure */ public static bool GetCmpBlockBounds(ref ChmFileInfo h, UInt64 block, ref UInt64 start, ref Int64 len) { byte[] buffer = new byte[8]; uint remain; uint pos = 0; /* for all but the last block, use the reset table */ if (block < h.reset_table.block_count - 1) { /* unpack the start address */ pos = 0; remain = 8; if (Storage.FetchBytes(ref h, ref buffer, (UInt64)h.data_offset + (UInt64)h.rt_unit.start + (UInt64)h.reset_table.table_offset + (UInt64)block * 8, remain) != remain || !Unmarshal.ToUInt64(ref buffer, ref pos, ref remain, ref start)) { return(false); } /* unpack the end address */ pos = 0; remain = 8; if (Storage.FetchBytes(ref h, ref buffer, (UInt64)h.data_offset + (UInt64)h.rt_unit.start + (UInt64)h.reset_table.table_offset + (UInt64)block * 8 + 8, remain) != remain || !Unmarshal.ToInt64(ref buffer, ref pos, ref remain, ref len)) { return(false); } } /* for the last block, use the span in addition to the reset table */ else { /* unpack the start address */ pos = 0; remain = 8; if (Storage.FetchBytes(ref h, ref buffer, (UInt64)h.data_offset + (UInt64)h.rt_unit.start + (UInt64)h.reset_table.table_offset + (UInt64)block * 8, remain) != remain || !Unmarshal.ToUInt64(ref buffer, ref pos, ref remain, ref start)) { return(false); } len = (Int64)h.reset_table.compressed_len; } /* compute the length and absolute start address */ len -= (Int64)start; start += h.data_offset + h.cn_unit.start; return(true); }
/* grab a region from a compressed block */ public static Int64 DecompressRegion(ref ChmFileInfo h, ref byte[] buf, ulong bufpos, UInt64 start, Int64 len) { UInt64 nBlock, nOffset; UInt64 nLen; UInt64 gotLen; byte[] ubuffer = null; if (len <= 0) { return((Int64)0); } /* figure out what we need to read */ nBlock = start / h.reset_table.block_len; nOffset = start % h.reset_table.block_len; nLen = (LONGUINT64)len; if (nLen > (h.reset_table.block_len - nOffset)) { nLen = h.reset_table.block_len - nOffset; } /* if block is cached, return data from it. */ h.lzx_mutex.WaitOne(); h.cache_mutex.WaitOne(); if (h.cache_block_indices[nBlock % (LONGUINT64)h.cache_num_blocks] == nBlock && h.cache_blocks[nBlock % (LONGUINT64)h.cache_num_blocks] != null) { Array.Copy( h.cache_blocks[nBlock % (LONGUINT64)h.cache_num_blocks], (int)nOffset, buf, (int)bufpos, (long)nLen); h.cache_mutex.ReleaseMutex(); h.lzx_mutex.ReleaseMutex(); return((Int64)nLen); } h.cache_mutex.ReleaseMutex(); /* data request not satisfied, so... start up the decompressor machine */ if (!h.lzx_init) { int window_size = ffs(h.window_size) - 1; h.lzx_last_block = -1; h.lzx_state = Lzx.LZXinit(window_size); h.lzx_init = true; } /* decompress some data */ gotLen = (UInt64)DecompressBlock(ref h, nBlock, ref ubuffer); if (gotLen < nLen) { nLen = gotLen; } Array.Copy(ubuffer, (int)nOffset, buf, (int)bufpos, (int)nLen); h.lzx_mutex.ReleaseMutex(); return((Int64)nLen); }