Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1375 lines
33 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1991 - 1998.
  5. //
  6. // File: COMPRESS.CXX
  7. //
  8. // Contents: Compressor/Decompressor of data
  9. //
  10. // Classes: CCompress, CDecompress
  11. //
  12. // History: 12-Jun-91 BartoszM Created
  13. // 20-Jun-91 reviewed
  14. // 07-Aug-91 BartoszM Introduced Blocks
  15. // 28-May-92 KyleP Added compression.
  16. // 09-Dec-97 dlee Added PROPID and LZ compression
  17. //
  18. //----------------------------------------------------------------------------
  19. #include <pch.cxx>
  20. #pragma hdrstop
  21. #include "compress.hxx"
  22. const BYTE pidId2Byte = 0x00; // next 2 bytes is the PROPID
  23. const BYTE pidId4Byte = 0xff; // next 4 bytes is the PROPID
  24. const flagKeyPrefix = 0x1f; // mask; 0x1f if prefix len stored later
  25. const flagKeyCompressed = 0x20; // 1 == unicode compressed, 0 == not
  26. const flagKeyZeroFirst = 0x40; // 1 == compressed & put zeros first
  27. const flagKeyPidContents = 0x80; // 1 == pidContents, 0 == pid stored
  28. template<class T> BOOL isOdd(T value) { return 0 != (value & 1); }
  29. //+-------------------------------------------------------------------------
  30. //
  31. // Member: CBlock::GetFirstKey, public
  32. //
  33. // Effects: Stores the first key of the block in [key]
  34. //
  35. // Arguments: [key] -- Key buffer to be filled in.
  36. //
  37. // Requires: A key must start on this block.
  38. //
  39. // History: 28-May-92 KyleP Crated
  40. //
  41. //--------------------------------------------------------------------------
  42. void CBlock::GetFirstKey( CKeyBuf& key )
  43. {
  44. DeCompress();
  45. unsigned off = _offFirstKey;
  46. Win4Assert( off != offInvalid );
  47. BYTE flags = _pData[off++];
  48. Win4Assert( 0 == ( flags & flagKeyPrefix ) );
  49. unsigned cbSuffix = _pData[ off++ ];
  50. BYTE * pbKey = key.GetWritableBuf();
  51. if ( 0 != ( flags & flagKeyCompressed ) )
  52. {
  53. Win4Assert( 0 != ( flags & flagKeyZeroFirst ) );
  54. unsigned cbKey = 1 + cbSuffix * 2;
  55. key.SetCount( cbKey );
  56. *pbKey++ = STRING_KEY;
  57. for ( unsigned i = 0; i < cbSuffix; i++ )
  58. {
  59. *pbKey++ = 0;
  60. *pbKey++ = _pData[ off++ ];
  61. }
  62. }
  63. else
  64. {
  65. key.SetCount( cbSuffix );
  66. memcpy( pbKey, _pData + off, cbSuffix );
  67. off += cbSuffix;
  68. }
  69. if ( 0 != ( flags & flagKeyPidContents ) )
  70. {
  71. key.SetPid( pidContents );
  72. }
  73. else
  74. {
  75. BYTE pidInfo = *(_pData+off);
  76. off++;
  77. PROPID pid;
  78. if ( pidId2Byte == pidInfo )
  79. {
  80. USHORT usPid;
  81. LoadUSHORT( _pData + off, usPid );
  82. pid = usPid ;
  83. }
  84. else if ( pidId4Byte == pidInfo )
  85. LoadULONG( _pData + off, pid );
  86. else if ( pidInfo > pidIdMaxSmallPid )
  87. pid = pidInfo - pidIdMaxSmallPid + pidNewPidBase - 1;
  88. else
  89. pid = pidInfo;
  90. Win4Assert( pid != pidAll );
  91. key.SetPid ( pid );
  92. }
  93. } //GetFirstKey
  94. // wordlist compression is now good enough that rtl doesn't give us much
  95. // given its cost including the 32k scratch pad and 3% total cpu overhead.
  96. #define CI_COMPRESS_SORT_BLOCKS 0
  97. #if CI_COMPRESS_SORT_BLOCKS
  98. // prevent multiple simultaneous compressions / decompressions
  99. CStaticMutexSem g_mtxCompression;
  100. BYTE g_CompressionWorkspace[ 32784 ]; // don't ask
  101. #endif //CI_COMPRESS_SORT_BLOCKS
  102. //+-------------------------------------------------------------------------
  103. //
  104. // Function: DeCompress
  105. //
  106. // Effects: Decompresses the buffer
  107. //
  108. // History: 4-Dec-97 dlee Crated
  109. //
  110. //--------------------------------------------------------------------------
  111. void CBlock::DeCompress()
  112. {
  113. #if CI_COMPRESS_SORT_BLOCKS
  114. CLock lock( g_mtxCompression );
  115. if ( !_fCompressed )
  116. return;
  117. //
  118. // Alpha decompress touches bytes up to a quadword byte boundary
  119. // beyond what it should. This is a feature.
  120. //
  121. XArray<BYTE> aOut( AlignBlock( _cbInUse, sizeof LONGLONG ) );
  122. ULONG cbOut = 0;
  123. NTSTATUS s = RtlDecompressBuffer( COMPRESSION_FORMAT_LZNT1,
  124. aOut.GetPointer(),
  125. (ULONG) _cbInUse,
  126. _pData,
  127. (ULONG) _cbCompressed,
  128. &cbOut );
  129. if ( NT_ERROR( s ) )
  130. {
  131. ciDebugOut(( DEB_WARN, "failed to decompress 0x%x CBlock: 0x%x, cbOut: 0x%x\n",
  132. this, s, cbOut ));
  133. Win4Assert( FALSE );
  134. THROW( CException( s ) );
  135. }
  136. ciDebugOut(( DEB_ITRACE, "decompressed from %d to %d\n",
  137. (ULONG) _cbCompressed, (ULONG) _cbInUse ));
  138. Win4Assert( cbOut == _cbInUse );
  139. _fCompressed = FALSE;
  140. delete [] _pData;
  141. _pData = aOut.Acquire();
  142. #else
  143. Win4Assert( !_fCompressed );
  144. #endif
  145. } //DeCompress
  146. //+-------------------------------------------------------------------------
  147. //
  148. // Function: Compress
  149. //
  150. // Effects: Creates a new, smaller block from an old block
  151. //
  152. // History: 4-Dec-97 dlee Crated
  153. //
  154. //--------------------------------------------------------------------------
  155. #if CIDBG == 1
  156. ULONG g_cbUnCompressedTotal = 0;
  157. ULONG g_cbUnCompressed = 0;
  158. ULONG g_cbCompressed = 0;
  159. #endif // CIDBG == 1
  160. void CBlock::Compress()
  161. {
  162. Win4Assert( !_fCompressed );
  163. #if CI_COMPRESS_SORT_BLOCKS
  164. #if CIDBG == 1
  165. // Assert the workspace is big enough
  166. ULONG cbWork = 0, cbFragment;
  167. RtlGetCompressionWorkSpaceSize( COMPRESSION_FORMAT_LZNT1,
  168. &cbWork,
  169. &cbFragment );
  170. // cbWork is 32784 on current builds
  171. Win4Assert( cbWork <= sizeof g_CompressionWorkspace );
  172. #endif // CIDBG == 1
  173. // Compress the data since it may not be needed for a long time.
  174. LONGLONG aOut[ cbInitialBlock / sizeof LONGLONG ];
  175. ULONG cbOut = 0;
  176. CLock lock( g_mtxCompression );
  177. NTSTATUS s = RtlCompressBuffer( COMPRESSION_FORMAT_LZNT1,
  178. _pData,
  179. _cbInUse,
  180. (BYTE *) aOut,
  181. sizeof aOut,
  182. cbInitialBlock,
  183. &cbOut,
  184. g_CompressionWorkspace );
  185. //
  186. // ignore failures to compress -- leave it uncompressed
  187. //
  188. if ( NT_SUCCESS( s ) && ( cbOut < _cbInUse ) )
  189. {
  190. BYTE * pNew = new BYTE[ cbOut ];
  191. RtlCopyMemory( pNew, aOut, cbOut );
  192. delete [] _pData;
  193. _pData = pNew;
  194. Win4Assert( cbOut <= 0xffff );
  195. _cbCompressed = (USHORT) cbOut;
  196. _fCompressed = TRUE;
  197. ciDebugOut(( DEB_ITRACE, "shrink block %d to %d\n",
  198. (ULONG) _cbInUse, (ULONG) _cbCompressed ));
  199. #if CIDBG == 1
  200. g_cbUnCompressedTotal += cbInitialBlock;
  201. g_cbUnCompressed += _cbInUse;
  202. g_cbCompressed += _cbCompressed;
  203. #endif // CIDBG == 1
  204. }
  205. else if ( NT_ERROR( s ) )
  206. {
  207. ciDebugOut(( DEB_WARN, "didn't compress: 0x%x\n", s ));
  208. }
  209. #else
  210. // Make the buffer as big as it needs to be
  211. #if CIDBG == 1
  212. g_cbUnCompressedTotal += cbInitialBlock;
  213. g_cbUnCompressed += _cbInUse;
  214. #endif // CIDBG == 1
  215. XArray<BYTE> aTmp( _cbInUse );
  216. RtlCopyMemory( aTmp.GetPointer(), _pData, _cbInUse );
  217. delete [] _pData;
  218. _pData = aTmp.Acquire();
  219. #endif
  220. //
  221. // pitch the memory out of the working set
  222. //
  223. VirtualUnlock( _pData, _fCompressed ? _cbCompressed : _cbInUse );
  224. } //Compress
  225. //+-------------------------------------------------------------------------
  226. //
  227. // Member: CBlock::CompressList, public
  228. //
  229. // Effects: Shrinks blocks in the list, saving memory
  230. //
  231. // History: 4-Dec-97 dlee Crated
  232. //
  233. //--------------------------------------------------------------------------
  234. void CBlock::CompressList()
  235. {
  236. CBlock * pCur = this;
  237. while ( 0 != pCur )
  238. {
  239. pCur->Compress();
  240. pCur = pCur->_pNext;
  241. }
  242. } //CompressList
  243. //+---------------------------------------------------------------------------
  244. //
  245. // Member: CCompress::CCompress, public
  246. //
  247. // Synopsis: initialize data
  248. //
  249. // Arguments: [fMultipleWid] -- TRUE if > 1 WorkId will be stored.
  250. //
  251. // History: 12-Jun-91 BartoszM Created
  252. // 28-May-92 KyleP One step construction.
  253. //
  254. //----------------------------------------------------------------------------
  255. CCompress::CCompress ()
  256. : _cKeyBlock( 0 ),
  257. _widCount( 0 ),
  258. _occCount( 0 ),
  259. _pWidCount( 0 ),
  260. _pOccCount( 0 )
  261. {
  262. _block = new CBlock;
  263. _buf = _block->Buffer();
  264. _cur = _buf;
  265. _lastKey.SetCount( 0 ); // No prefix from previous key.
  266. }
  267. //+---------------------------------------------------------------------------
  268. //
  269. // Member: CCompress::GetFirstBlock, public
  270. //
  271. // Synopsis: stores first entry and initializes cursors
  272. //
  273. // History: 28-May-92 KyleP Created
  274. //
  275. //----------------------------------------------------------------------------
  276. CBlock * CCompress::GetFirstBlock()
  277. {
  278. return _block;
  279. }
  280. //+---------------------------------------------------------------------------
  281. //
  282. // Member: CCompress::~CCompress, public
  283. //
  284. // Synopsis: write last wid and occ counts
  285. //
  286. // History: 12-Jun-91 BartoszM Created
  287. // 28-May-92 KyleP Restructured. Added compression.
  288. //
  289. //----------------------------------------------------------------------------
  290. CCompress::~CCompress()
  291. {
  292. IPutOccCount();
  293. IPutWidCount();
  294. _block->_cbInUse = (USHORT)(_cur - _buf);
  295. }
  296. //+---------------------------------------------------------------------------
  297. //
  298. // Member: CCompress::PutKey, public
  299. //
  300. // Synopsis: store compressed key
  301. //
  302. // Arguments: [cb] - size of key
  303. // [buf] - key buffer
  304. // [pid] - property id
  305. //
  306. // History: 12-Jun-91 BartoszM Created
  307. // 28-May-92 KyleP Restructured. Added compression.
  308. //
  309. //----------------------------------------------------------------------------
  310. void CCompress::PutKey ( unsigned cb, const BYTE* buf, PROPID pid )
  311. {
  312. ciAssert ( cb != 0 );
  313. IPutWidCount();
  314. IPutKey( cb, buf, pid );
  315. IAllocWidCount();
  316. }
  317. //+---------------------------------------------------------------------------
  318. //
  319. // Member: CCompress::PutWid, public
  320. //
  321. // Synopsis: store compressed work id
  322. //
  323. // Arguments: [wid] - work id
  324. //
  325. // History: 12-Jun-91 BartoszM Created
  326. // 28-May-92 KyleP Restructured. Added compression.
  327. //
  328. //----------------------------------------------------------------------------
  329. void CCompress::PutWid ( WORKID wid )
  330. {
  331. ciAssert( wid > 0 && wid < 256 ); // Has to fit in a byte
  332. ciAssert( _widCount < 255 ); // Has to fit in a byte
  333. IPutOccCount();
  334. IPutWid( wid );
  335. IAllocOccCount();
  336. }
  337. //+---------------------------------------------------------------------------
  338. //
  339. // Member: CCompress::PutOcc, public
  340. //
  341. // Synopsis: store compressed occurrence
  342. //
  343. // Arguments: [occ] - occurrence
  344. //
  345. // History: 12-Jun-91 BartoszM Created
  346. // 20-May-92 KyleP Occurrence compression
  347. // 28-May-92 KyleP Restructured. Added compression.
  348. //
  349. //----------------------------------------------------------------------------
  350. void CCompress::PutOcc ( OCCURRENCE occ )
  351. {
  352. IPutOcc( occ );
  353. }
  354. //+---------------------------------------------------------------------------
  355. //
  356. // Member: CCompress::AllocNewBlock, private
  357. //
  358. // Synopsis: Allocate and link in new block
  359. //
  360. // History: 07-Aug-91 BartoszM Created
  361. //
  362. //----------------------------------------------------------------------------
  363. void CCompress::AllocNewBlock ()
  364. {
  365. // ciDebugOut (( DEB_ITRACE, "Compress:: Alloc new block\n" ));
  366. _block->_cbInUse = (USHORT)(_cur - _buf);
  367. _block->_pNext = new CBlock;
  368. _block = _block->_pNext;
  369. _buf = _block->_pData;
  370. _cur = _buf;
  371. }
  372. //+-------------------------------------------------------------------------
  373. //
  374. // Member: CCompress::IPutWidCount, private
  375. //
  376. // Synopsis: Backpatches the workid count.
  377. //
  378. // History: 28-May-92 KyleP Created
  379. //
  380. //--------------------------------------------------------------------------
  381. void CCompress::IPutWidCount()
  382. {
  383. if ( _pWidCount )
  384. *_pWidCount = (BYTE)_widCount;
  385. _widCount = 0;
  386. }
  387. //+-------------------------------------------------------------------------
  388. //
  389. // Member: CCompress::IPutKey, private
  390. //
  391. // Synopsis: Stores a key.
  392. //
  393. // Arguments: [cb] -- Count in bytes of [buf]
  394. // [buf] -- Key.
  395. // [pid] -- Property ID.
  396. //
  397. // History: 28-May-92 KyleP Created
  398. //
  399. // Notes: Format for a key is:
  400. // 1 BYTE -- flags, prefix byte count if < 0xf
  401. // 1 BYTE -- # bytes prefix (from previous key) (optional)
  402. // 1 BYTE -- # bytes suffix (from this key)
  403. // n BYTE -- The suffix (possibly Unicode compressed)
  404. // 1-5 bytes -- Property Id. (if not pidContents)
  405. //
  406. //--------------------------------------------------------------------------
  407. void CCompress::IPutKey( unsigned cb, BYTE const * buf, PROPID pid )
  408. {
  409. //
  410. // Calculate the common prefix from the previous key.
  411. //
  412. UINT mincb = __min( _lastKey.Count(), cb );
  413. UINT cPrefix;
  414. for ( cPrefix = 0;
  415. (cPrefix < mincb) &&
  416. ((_lastKey.GetBuf())[cPrefix] == buf[cPrefix]);
  417. cPrefix++)
  418. continue;
  419. UINT cSuffix = cb - cPrefix;
  420. memcpy( _lastKey.GetWritableBuf() + cPrefix, buf + cPrefix, cSuffix );
  421. _lastKey.SetCount( cb );
  422. _lastKey.SetPid( pid );
  423. //
  424. // Out of space?
  425. //
  426. if ( !KeyWillFit ( cSuffix ) )
  427. AllocNewBlock();
  428. if ( _block->_offFirstKey == offInvalid )
  429. {
  430. BackPatch ( (unsigned)(_cur - _buf) );
  431. cPrefix = 0;
  432. cSuffix = cb;
  433. }
  434. // See if the prefix length can be stored in the flag byte
  435. BYTE flags = ( cPrefix < flagKeyPrefix ) ? cPrefix : flagKeyPrefix;
  436. // pidContents is stored as a bit flag
  437. if ( pidContents == pid )
  438. flags |= flagKeyPidContents;
  439. // Check if the key can be Unicode compressed.
  440. BOOL fCompressible = FALSE;
  441. BYTE abCompressed[ MAXKEYSIZE / 2 ];
  442. unsigned cbCompressed = 0;
  443. if ( STRING_KEY == *buf )
  444. {
  445. Win4Assert( isOdd( cPrefix ) != isOdd( cSuffix ) );
  446. BYTE const * pbCompressCheck = buf;
  447. unsigned cSuffixTmp = cSuffix;
  448. if ( 0 == cPrefix )
  449. {
  450. pbCompressCheck++;
  451. cSuffixTmp--;
  452. }
  453. else
  454. {
  455. pbCompressCheck += cPrefix;
  456. }
  457. BYTE const * pbCheck = pbCompressCheck;
  458. BYTE const * pbAfter = buf + cb;
  459. fCompressible = TRUE;
  460. if ( isOdd( cSuffixTmp ) )
  461. {
  462. while ( pbCheck < pbAfter )
  463. {
  464. abCompressed[ cbCompressed++ ] = *pbCheck++;
  465. if ( pbCheck < pbAfter )
  466. {
  467. if ( 0 != *pbCheck )
  468. {
  469. fCompressible = FALSE;
  470. break;
  471. }
  472. else
  473. pbCheck++;
  474. }
  475. }
  476. }
  477. else
  478. {
  479. while ( pbCheck < pbAfter )
  480. {
  481. if ( 0 != *pbCheck )
  482. {
  483. fCompressible = FALSE;
  484. break;
  485. }
  486. abCompressed[ cbCompressed++ ] = * ( pbCheck + 1 );
  487. pbCheck += 2;
  488. }
  489. if ( fCompressible )
  490. flags |= flagKeyZeroFirst;
  491. }
  492. if ( fCompressible )
  493. flags |= flagKeyCompressed;
  494. }
  495. // Store the flags
  496. *_cur++ = flags;
  497. // Store the prefix length if it didn't fit in the flags field
  498. if ( cPrefix >= flagKeyPrefix )
  499. *_cur++ = (BYTE) cPrefix;
  500. //
  501. // Store the key.
  502. //
  503. if ( fCompressible )
  504. {
  505. *_cur++ = (BYTE) cbCompressed;
  506. memcpy( _cur, abCompressed, cbCompressed );
  507. _cur += cbCompressed;
  508. }
  509. else
  510. {
  511. *_cur++ = (BYTE) cSuffix;
  512. memcpy( _cur, buf + cPrefix, cSuffix );
  513. _cur += cSuffix;
  514. }
  515. //
  516. // store the pid in 1, 3, or 5 bytes
  517. // note: pids 0 and 0xff are reserved as markers
  518. //
  519. // pids 1 - 0x40 are stored as is in 1 byte
  520. // pids 0x1000 to 0x10bd are stored as 1 byte 0x41 to 0xfe
  521. // other pids < 0x10000 are stored as pidID2Byte followed by 2 bytes
  522. // pids >= 0x10000 are stored as pidID4Byte followed by 4 bytes
  523. //
  524. Win4Assert( pidAll != pid );
  525. if ( pidContents != pid )
  526. {
  527. if ( pid <= pidIdMaxSmallPid )
  528. {
  529. *_cur++ = (BYTE) pid;
  530. }
  531. else if ( ( pid >= pidNewPidBase ) &&
  532. ( pid < ( pidNewPidBase + 0xfd - pidIdMaxSmallPid ) ) )
  533. {
  534. *_cur++ = (BYTE) ( pid - pidNewPidBase + pidIdMaxSmallPid + 1 );
  535. }
  536. else if ( pid < 0x10000 )
  537. {
  538. // 1 byte id plus 2 bytes of pid
  539. *_cur++ = pidId2Byte;
  540. _cur += StoreUSHORT( _cur, (USHORT) pid );
  541. }
  542. else
  543. {
  544. // 1 byte id plus 4 bytes of pid
  545. *_cur++ = pidId4Byte;
  546. _cur += StoreULONG( _cur, pid );
  547. }
  548. }
  549. } //IPutKey
  550. //+-------------------------------------------------------------------------
  551. //
  552. // Member: CCompress::IAllocWidCount, private
  553. //
  554. // Synopsis: Allocates space for WorkId count.
  555. //
  556. // History: 28-May-92 KyleP Created
  557. //
  558. //--------------------------------------------------------------------------
  559. void CCompress::IAllocWidCount()
  560. {
  561. //
  562. // space for wid count
  563. // Note: the actual BYTE space has been pre-reserved in PutKey -- there
  564. // is room for the wid, so we don't have to check and AllocNewBlock.
  565. //
  566. Win4Assert( WidWillFit() );
  567. _pWidCount = _cur;
  568. _cur++;
  569. _lastWid = widInvalid;
  570. }
  571. //+-------------------------------------------------------------------------
  572. //
  573. // Member: CCompress::IPutWid, private
  574. //
  575. // Synopsis: Stores a WorkId
  576. //
  577. // Arguments: [wid] -- WorkId
  578. //
  579. // History: 28-May-92 KyleP Created
  580. //
  581. //--------------------------------------------------------------------------
  582. void CCompress::IPutWid( WORKID wid )
  583. {
  584. _widCount++;
  585. if ( ! WidAndOccCountWillFit() )
  586. AllocNewBlock();
  587. _lastWid = wid;
  588. *_cur = BYTE( wid );
  589. _cur++;
  590. }
  591. //+---------------------------------------------------------------------------
  592. //
  593. // Member: CCompress::IPutOccCount, public
  594. //
  595. // Synopsis: Write previous occurrence count and allocate new one.
  596. //
  597. // History: 20-May-92 KyleP Created
  598. //
  599. //----------------------------------------------------------------------------
  600. void CCompress::IPutOccCount()
  601. {
  602. //
  603. // Write previous occurence count
  604. //
  605. if ( _pOccCount )
  606. StoreUINT( _pOccCount, _occCount );
  607. _occCount = 0;
  608. _lastOcc = 0;
  609. }
  610. //+-------------------------------------------------------------------------
  611. //
  612. // Member: CCompress::IAllocOccCount, private
  613. //
  614. // Synopsis: Allocates space for occurrence count.
  615. //
  616. // History: 28-May-92 KyleP Created
  617. //
  618. //--------------------------------------------------------------------------
  619. void CCompress::IAllocOccCount()
  620. {
  621. //
  622. // Allocate space for new occ count. Note that for the many-wid
  623. // compressor this has been pre-reserved and will succeed.
  624. //
  625. if ( ! OccCountWillFit() )
  626. AllocNewBlock();
  627. _pOccCount = _cur;
  628. _cur += sizeof (unsigned);
  629. }
  630. //+-------------------------------------------------------------------------
  631. //
  632. // Member: CCompress::IPutOcc, private
  633. //
  634. // Synopsis: Stores an occurrence delta
  635. //
  636. // Arguments: [occ] -- Occurrence
  637. //
  638. // History: 28-May-92 KyleP Created
  639. //
  640. // Notes: Format for an occurrence delta is:
  641. //
  642. // delta <= 255 -- Store as BYTE
  643. // delta <= 65535 -- Store as 0 BYTE + 2 BYTES
  644. // delta > 65535 -- Store as 3 0 BYTES + 4 BYTES
  645. //
  646. //--------------------------------------------------------------------------
  647. void CCompress::IPutOcc( OCCURRENCE occ )
  648. {
  649. _occCount++;
  650. OCCURRENCE occDelta = occ - _lastOcc;
  651. _lastOcc = occ;
  652. ciAssert( occDelta != 0 );
  653. if ( !OccWillFit( occDelta ) )
  654. AllocNewBlock();
  655. if ( occDelta <= (1 << 8) - 1 )
  656. {
  657. *_cur = BYTE( occDelta );
  658. _cur++;
  659. }
  660. else if ( occDelta <= (1 << 16) - 1 )
  661. {
  662. ciAssert( sizeof( USHORT ) == 2 );
  663. *_cur++ = 0;
  664. _cur += StoreUSHORT( _cur, (USHORT)occDelta );
  665. }
  666. else
  667. {
  668. ciAssert( sizeof( ULONG ) == 4 );
  669. *_cur++ = 0;
  670. _cur += StoreUSHORT( _cur, 0 );
  671. _cur += StoreULONG( _cur, occDelta );
  672. }
  673. }
  674. //+---------------------------------------------------------------------------
  675. //
  676. // Member: COneWidCompress::PutKey, public
  677. //
  678. // Synopsis: store compressed key
  679. //
  680. // Arguments: [cb] - size of key
  681. // [buf] - key buffer
  682. // [pid] - property id
  683. //
  684. // History: 28-May-92 KyleP Created
  685. //
  686. //----------------------------------------------------------------------------
  687. void COneWidCompress::PutKey ( unsigned cb, const BYTE* buf, PROPID pid )
  688. {
  689. ciAssert ( cb != 0 );
  690. IPutOccCount();
  691. IPutKey( cb, buf, pid );
  692. IAllocOccCount();
  693. }
  694. //+---------------------------------------------------------------------------
  695. //
  696. // Member: COneWidCompress::PutWid, public
  697. //
  698. // Synopsis: store compressed work id
  699. //
  700. // Arguments: [wid] - work id
  701. //
  702. // History: 28-May-92 KyleP Created
  703. //
  704. // Notes: Storing a work id is an illegal operation for the one
  705. // wid compressor. This method can actually disappear in
  706. // the retail version.
  707. //
  708. //----------------------------------------------------------------------------
  709. #if CIDBG == 1
  710. void COneWidCompress::PutWid ( WORKID )
  711. {
  712. ciAssert( FALSE );
  713. }
  714. #endif // CIDBG == 1
  715. //+---------------------------------------------------------------------------
  716. //
  717. // Member: COneWidCompress::PutOcc, public
  718. //
  719. // Synopsis: store compressed occurrence
  720. //
  721. // Arguments: [occ] - occurrence
  722. //
  723. // History: 28-May-92 KyleP Created
  724. //
  725. //----------------------------------------------------------------------------
  726. void COneWidCompress::PutOcc ( OCCURRENCE occ )
  727. {
  728. IPutOcc( occ );
  729. }
  730. //+-------------------------------------------------------------------------
  731. //
  732. // Member: CDecompress::Init, public
  733. //
  734. // Synopsis: Initialize decompressor.
  735. //
  736. // Effects: Loads the first Key, WorkId, etc. in the block.
  737. //
  738. // Arguments: [block] -- Block to decompress.
  739. //
  740. // Requires: A key must start in [block].
  741. //
  742. // History: 28-May-92 KyleP Created
  743. //
  744. // Notes: CDecompress needs a two stage construction because we
  745. // don't know at cursor creation time what block will be
  746. // decompressed yet we want the decompressor inside the
  747. // cursor (e.g. no pointer)
  748. //
  749. //--------------------------------------------------------------------------
  750. void CDecompress::Init ( CBlock* block )
  751. {
  752. block->DeCompress();
  753. _block = block;
  754. _buf = _block->_pData;
  755. _cbInUse = _block->_cbInUse;
  756. ciAssert ( _block->_offFirstKey != offInvalid );
  757. _cur = _buf + _block->_offFirstKey;
  758. LoadKey();
  759. LoadWidCount();
  760. LoadWid();
  761. LoadOccCount();
  762. LoadOcc();
  763. }
  764. //+---------------------------------------------------------------------------
  765. //
  766. // Member: CDecompress::GetNextKey, public
  767. //
  768. // Synopsis: Advance to next key, cache it, and return it
  769. //
  770. // History: 12-Jun-91 BartoszM Created
  771. // 28-May-92 KyleP Added compression. Restructured.
  772. //
  773. //----------------------------------------------------------------------------
  774. const CKeyBuf* CDecompress::GetNextKey()
  775. {
  776. if ( !KeyExists() )
  777. return 0;
  778. //
  779. // skip remaining work id's
  780. //
  781. while ( NextWorkId() != widInvalid )
  782. continue; // Null body
  783. LoadKey();
  784. if ( KeyExists() )
  785. {
  786. LoadWidCount();
  787. LoadWid();
  788. LoadOccCount();
  789. LoadOcc();
  790. }
  791. return GetKey();
  792. }
  793. //+---------------------------------------------------------------------------
  794. //
  795. // Member: CDecompress::NextWorkId, public
  796. //
  797. // Synopsis: Advance to next work id, cache it, and return it
  798. //
  799. // History: 12-Jun-91 BartoszM Created
  800. // 28-May-92 KyleP Added compression. Restructured.
  801. //
  802. //----------------------------------------------------------------------------
  803. WORKID CDecompress::NextWorkId()
  804. {
  805. ciAssert ( KeyExists() );
  806. //
  807. // Skip the remaining occurrences
  808. //
  809. while ( NextOccurrence() != OCC_INVALID ); // Null body
  810. if ( _widCountLeft == 0 )
  811. {
  812. _curWid = widInvalid;
  813. _curOcc = OCC_INVALID;
  814. }
  815. else
  816. {
  817. LoadWid();
  818. LoadOccCount();
  819. LoadOcc();
  820. }
  821. return _curWid;
  822. }
  823. void CDecompress::RatioFinished (ULONG& denom, ULONG& num)
  824. {
  825. denom = _widCount;
  826. Win4Assert ( _widCount >= _widCountLeft );
  827. num = _widCount - _widCountLeft;
  828. }
  829. //+---------------------------------------------------------------------------
  830. //
  831. // Member: CDecompress::NextOccurrence, public
  832. //
  833. // Synopsis: Advance to next occurrence, return it
  834. //
  835. // History: 12-Jun-91 BartoszM Created
  836. // 28-May-92 KyleP Added compression. Restructured.
  837. //
  838. //----------------------------------------------------------------------------
  839. OCCURRENCE CDecompress::NextOccurrence()
  840. {
  841. ciAssert ( KeyExists() );
  842. if ( _occCountLeft == 0 )
  843. _curOcc = OCC_INVALID;
  844. else
  845. LoadOcc();
  846. return _curOcc;
  847. }
  848. //+---------------------------------------------------------------------------
  849. //
  850. // Member: CDecompress::LoadNextBlock, private
  851. //
  852. // Synopsis: Follow link to next block
  853. //
  854. // History: 07-Aug-91 BartoszM Created
  855. //
  856. //----------------------------------------------------------------------------
  857. BOOL CDecompress::LoadNextBlock ()
  858. {
  859. //
  860. // don't recompress _block here -- other queries or merges may be
  861. // using the block, and there is no refcount.
  862. //
  863. if ( _block->_pNext == 0 )
  864. {
  865. _curKey.SetCount(0);
  866. return FALSE;
  867. }
  868. _block = _block->_pNext;
  869. _block->DeCompress();
  870. _buf = _block->_pData;
  871. _cbInUse = _block->_cbInUse;
  872. _cur = _buf;
  873. return TRUE;
  874. }
  875. //+---------------------------------------------------------------------------
  876. //
  877. // Member: CDecompress::LoadKey, private
  878. //
  879. // Synopsis: Cache key wid and occurrence under cursor
  880. //
  881. // History: 12-Jun-91 BartoszM Created
  882. // 28-May-92 KyleP Added compression. Restructured.
  883. //
  884. // Notes: End of block can be either before the beginning
  885. // of the key or after wid count.
  886. //
  887. //----------------------------------------------------------------------------
  888. void CDecompress::LoadKey()
  889. {
  890. if ( EndBlock() && !LoadNextBlock() )
  891. return;
  892. BYTE flags = *_cur++;
  893. UINT cPrefix = ( flagKeyPrefix & flags );
  894. if ( flagKeyPrefix == cPrefix )
  895. cPrefix = *_cur++;
  896. UINT cSuffix = *_cur++;
  897. BYTE * pbKey = _curKey.GetWritableBuf() + cPrefix;
  898. UINT cbKey = cPrefix;
  899. if ( 0 != ( flagKeyCompressed & flags ) )
  900. {
  901. cbKey += cSuffix * 2;
  902. if ( 0 == cPrefix )
  903. {
  904. cbKey++;
  905. *pbKey++ = STRING_KEY;
  906. }
  907. BOOL fZeroFirst = ( 0 != ( flags & flagKeyZeroFirst ) );
  908. if ( !fZeroFirst )
  909. {
  910. *pbKey++ = *_cur++;
  911. cbKey--;
  912. cSuffix--;
  913. }
  914. Win4Assert( isOdd( cbKey ) );
  915. for ( unsigned i = 0; i < cSuffix; i++ )
  916. {
  917. *pbKey++ = 0;
  918. *pbKey++ = *_cur++;
  919. }
  920. }
  921. else
  922. {
  923. memcpy( pbKey, _cur, cSuffix );
  924. _cur += cSuffix;
  925. cbKey += cSuffix;
  926. }
  927. _curKey.SetCount( cbKey );
  928. if ( 0 != ( flags & flagKeyPidContents ) )
  929. {
  930. _curKey.SetPid( pidContents );
  931. }
  932. else
  933. {
  934. BYTE pidInfo = *_cur++;
  935. PROPID pid;
  936. if ( pidId2Byte == pidInfo )
  937. {
  938. USHORT usPid;
  939. _cur += LoadUSHORT( _cur, usPid );
  940. pid = usPid;
  941. }
  942. else if ( pidId4Byte == pidInfo )
  943. _cur += LoadULONG( _cur, pid );
  944. else if ( pidInfo > pidIdMaxSmallPid )
  945. pid = pidInfo - pidIdMaxSmallPid + pidNewPidBase - 1;
  946. else
  947. pid = pidInfo;
  948. Win4Assert( pid != pidAll );
  949. _curKey.SetPid( pid );
  950. }
  951. } //LoadKey
  952. //+-------------------------------------------------------------------------
  953. //
  954. // Member: CDecompress::LoadWidCount, private
  955. //
  956. // Synopsis: Loads workid count.
  957. //
  958. // History: 28-May-92 KyleP Created
  959. //
  960. //--------------------------------------------------------------------------
  961. void CDecompress::LoadWidCount()
  962. {
  963. if ( EndBlock() )
  964. LoadNextBlock();
  965. _widCount = *_cur;
  966. _widCountLeft = _widCount;
  967. _cur++;
  968. }
  969. //+---------------------------------------------------------------------------
  970. //
  971. // Member: CDecompress::LoadWid, private
  972. //
  973. // Synopsis: Cache work id and occurrence under cursor
  974. //
  975. // History: 12-Jun-91 BartoszM Created
  976. // 28-May-92 KyleP Added compression. Restructured.
  977. //
  978. //----------------------------------------------------------------------------
  979. void CDecompress::LoadWid ()
  980. {
  981. if ( EndBlock() )
  982. LoadNextBlock();
  983. ciAssert ( KeyExists() );
  984. _curWid = *_cur;
  985. _cur++;
  986. _widCountLeft--;
  987. }
  988. //+---------------------------------------------------------------------------
  989. //
  990. // Member: CDeCompress::LoadOccCount, private
  991. //
  992. // Synopsis: Cache occurrence count under cursor
  993. //
  994. // History: 21-May-92 KyleP Created
  995. //
  996. //----------------------------------------------------------------------------
  997. void CDecompress::LoadOccCount()
  998. {
  999. if ( EndBlock() )
  1000. LoadNextBlock();
  1001. ciAssert ( KeyExists() );
  1002. _cur += LoadUINT( _cur, _occCount );
  1003. _occCountLeft = _occCount;
  1004. _curOcc = 0;
  1005. }
  1006. //+---------------------------------------------------------------------------
  1007. //
  1008. // Member: CDeCompress::LoadOcc, private
  1009. //
  1010. // Synopsis: Cache occurrence under cursor
  1011. //
  1012. // Requires: valid occurrence under the cursor
  1013. //
  1014. // History: 12-Jun-91 BartoszM Created
  1015. // 28-May-92 KyleP Added compression. Restructured.
  1016. //
  1017. //----------------------------------------------------------------------------
  1018. void CDecompress::LoadOcc()
  1019. {
  1020. if ( EndBlock() )
  1021. LoadNextBlock();
  1022. ciAssert ( KeyExists() );
  1023. OCCURRENCE occDelta;
  1024. occDelta = *_cur++;
  1025. if ( occDelta == 0 )
  1026. {
  1027. _cur += LoadUSHORT( _cur, (USHORT &)occDelta );
  1028. if ( occDelta == 0 )
  1029. {
  1030. _cur += LoadULONG( _cur, occDelta );
  1031. }
  1032. }
  1033. _curOcc += occDelta;
  1034. _occCountLeft--;
  1035. }
  1036. //+-------------------------------------------------------------------------
  1037. //
  1038. // Member: COneWidDecompress::Init, public
  1039. //
  1040. // Synopsis: Initialize decompressor.
  1041. //
  1042. // Effects: Loads the first Key, Occurrence, etc. in the block.
  1043. //
  1044. // Arguments: [block] -- Block to decompress.
  1045. //
  1046. // Requires: A key must start in [block].
  1047. //
  1048. // History: 28-May-92 KyleP Created
  1049. //
  1050. // Notes: CDecompress needs a two stage construction because we
  1051. // don't know at cursor creation time what block will be
  1052. // decompressed yet we want the decompressor inside the
  1053. // cursor (e.g. no pointer)
  1054. //
  1055. //--------------------------------------------------------------------------
  1056. void COneWidDecompress::Init ( CBlock* block )
  1057. {
  1058. block->DeCompress();
  1059. _block = block;
  1060. _buf = _block->_pData;
  1061. _cbInUse = _block->_cbInUse;
  1062. ciAssert ( _block->_offFirstKey != offInvalid );
  1063. _cur = _buf + _block->_offFirstKey;
  1064. LoadKey();
  1065. LoadOccCount();
  1066. LoadOcc();
  1067. }
  1068. //+---------------------------------------------------------------------------
  1069. //
  1070. // Member: COneWidDecompress::GetNextKey, public
  1071. //
  1072. // Synopsis: Advance to next key, cache it, and return it
  1073. //
  1074. // History: 28-May-92 KyleP Created
  1075. //
  1076. //----------------------------------------------------------------------------
  1077. const CKeyBuf* COneWidDecompress::GetNextKey()
  1078. {
  1079. if ( !KeyExists() )
  1080. return 0;
  1081. //
  1082. // skip remaining occurrences
  1083. //
  1084. while ( NextOccurrence() != OCC_INVALID ); // Null body
  1085. LoadKey();
  1086. if ( KeyExists() )
  1087. {
  1088. LoadOccCount();
  1089. LoadOcc();
  1090. }
  1091. return GetKey();
  1092. }
  1093. //+---------------------------------------------------------------------------
  1094. //
  1095. // Member: COneWidDecompress::NextWorkId, public
  1096. //
  1097. // Synopsis: Advance to next work id, cache it, and return it
  1098. //
  1099. // History: 28-May-92 KyleP Created
  1100. //
  1101. //----------------------------------------------------------------------------
  1102. WORKID COneWidDecompress::NextWorkId()
  1103. {
  1104. ciAssert( FALSE );
  1105. return( widInvalid );
  1106. }
  1107. //+---------------------------------------------------------------------------
  1108. //
  1109. // Member: COneWidDecompress::NextOccurrence, public
  1110. //
  1111. // Synopsis: Advance to next occurrence, return it
  1112. //
  1113. // History: 28-May-92 KyleP Created
  1114. //
  1115. //----------------------------------------------------------------------------
  1116. OCCURRENCE COneWidDecompress::NextOccurrence()
  1117. {
  1118. ciAssert ( KeyExists() );
  1119. if ( _occCountLeft == 0 )
  1120. _curOcc = OCC_INVALID;
  1121. else
  1122. LoadOcc ();
  1123. return _curOcc;
  1124. }