Google
 

Trailing-Edge - PDP-10 Archives - BB-PBDEB-BB_1990 - 10,7/rms10/rmssrc/rmsspt.b36
There are 11 other files named rmsspt.b36 in the archive. Click here to see a list.
MODULE SPT =


BEGIN

%([

THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.

!COPYRIGHT (C) 1977, 1981 BY DIGITAL EQUIPMENT CORPORATION


PURPOSE:	ROUTINES ASSOCIATED WITH SPLITTING A DATA BUCKET.

AUTHOR:	S. BLOUNT /EGM/RL


**********	TABLE OF CONTENTS	**************




	ROUTINE			FUNCTION
	=======			========

	SPLIT			SPLIT A DATA BUCKET

	COMPRESS		COMPRESS A DATA BUCKET

	COMPRRV			COMPRESS RRV'S DURING BUCKET COMPRESSION

	UPDRRVS			UPDATE RRV RECORDS AFTER A SPLIT




REVISION HISTORY:

EDIT	DATE		WHO	PURPOSE
====	====		===	========

1	24-AUG-76	JK	ADD 'UPDRRVS' ROUTINE.
2	1-SEP-76	JK	REPLACE REFS TO ZERO ID BY 'NULLID'.
3	1-SEP-76	JK	FIX 'UPDRRVS'.
4	1-SEP-76	JK	FIX 'UPDRRVS' -- 'UPDBKD' SHOULD BE MAPPED 'FORMAT'.
5	2-SEP-76	JK	REMOVE EDIT 3 (EDIT 4 FOUND REAL CULPRIT).
6	2-SEP-76	JK	REMOVE EDIT 5, REINSTATE EDIT 3, UPDATE RRV REC. CORRECTLY.
7	2-SEP-76	JK	'UPDRRVS' NOW HANDLES "RRV NOT FOUND" CORRECTLY.
8	3-SEP-76	JK	SPLIT RMSUDR INTO RMSUDR, RMSUD2, RMSSPT.
9	3-SEP-76	JK	CHECK BUSYFLAG CORRECTLY IN 'UPDRRVS'.
10	8-SEP-76	JK	'UPDRRVS' NO LONGER USES 'FBYRFA' (REPLACED BY 'SDATABKT').
11	1-OCT-76	SB	MAKE SPLIT WORK ON SIDR'S
12	3-OCT-76	SB	CHANGE RDLENGTH INPUT TO SPLIT,...
13	1-NOV-76	SB	TAKE OUT FPROBLEM
14	11-NOV-76	SB	ID'S NOT ALLOCATED IN SIDR BKT, CLEAR TMPRFA
15	6-JAN-77	SB	UPDRRV'S DOESNT SET UPDRRV FLAG PROPERLY
16	1-FEB-77	SB	MAKE SPLIT WORK ON 1/2 OF RECORD SPACE,
				NOT 1/2 OF SIZE OF BUCKET.
17	4-FEB-77	SB	FIX SPLIT TO ADJUST LASTRHIGHPTR CORRECTLY
				IF THE LAST REC IS TOO BIG.
18	17-FEB-77	SB	SPLIT INTO 3 BKTS IF A DUP WONT FIT IN ORIGINAL BKT
19	28-FEB-77	SB	FIX BUG IF ALL DUPS ARE INSERTED (GAVE A 3-BKT)
20	8-MAR-77	SB	REMOVE FILEPROBLEM IN UPDRRVS
21	5-MAY-77	SB	FIX SPLIT SO THAT IF DUPS IN PRIMARY, SPLIT
				IS ALWAYS BEFORE OR AFTER NEW RECORD.

*************************************************
*						*
*		NEW REVISION HISTORY		*
*						*
*************************************************

PRODUCT	MODULE	 SPR
 EDIT	 EDIT	 QAR		DESCRIPTION
======	======	=====		===========

12	22	11439	SPLIT IS SETTING UP THE LAST RECORD PTR INCORRECTLY
			WHEN SCANRECS FALLS THRU ON THE FIRST TEST. THE
			POINTER ENDS UP AT THE CURRENT RECORD, NOT THE
			PREVIOUS ONE, BECAUSE RECORDSIZE IS INITIALIZED TO
			ZERO, NOT THE SIZE OF THE PREVIOUS RECORD.

15	23	11982	DURING A PUT TO AN INDEXED FILE WITH ALTERNATES
			WITH DUPLICATES, SPLIT FAILS TO MOVE THE LAST
			SIDR ARRAY FROM THE 1ST BUCKET TO THE 2ND WHEN
			THE LAST ARRAY IS GREATER THAN HALF THE SIZE OF
			THE BUCKET. THIS PRODUCES AN EMPTY BUCKET, AND
			CAUSES DOSIDR TO OVERWRITE 1 WORD OF WHATEVER
			FOLLOWS THE ORIGINAL BUCKET. ALSO, THE
			RECORDSIZE USED BY SCANRECS MUST BE INITIALIZED
			TO 0 IF THE CURRENT RECORD IS THE FIRST RECORD
			IN THE BUCKET.

******** Release of Version 1.0 *******

PRODUCT	MODULE	 SPR
 EDIT	 EDIT	 QAR		DESCRIPTION
======	======	=====		===========

54	24	20-17022	If a record is deleted and then rewritten
                                with a  greater length, RMS will try to insert
                                the new record immediately after the old one
                                and the DUPLICATES flag in the record
                                descriptor will be set.  If the bucket is
                                compressed, the deleted record will no longer
                                exist but the DUPLICATES flag will not be
                                updated. This can cause a 3-way split with no
                                index to the new record.  This is fixed by
                                checking for duplicates before
                                leaving COMPRESS.  RLUSK 24-DEC-81

****************** Start RMS-10 V1.1 *********************
********************* TOPS-10 ONLY ***********************

PRODUCT	MODULE	 SPR
 EDIT	 EDIT	 QAR		DESCRIPTION
======	======	=====		===========

 100	  25	Dev		Make declarations for routine names
				be EXTERNAL ROUTINE so RMS will compile 
				under BLISS V4 (RMT, 10/22/85).

 117	  26	10-35371	(asp, 10/7/86) Edit 54 (in COMPRESS) does
				not correctly check for duplicates as was
				intended.  Apply RL fix M460 12-Mar-84.

 125	27	10-36171	In COMPRESS, check first for end of bucket
				(page) so the check for RRV flag doesn't
				try to touch a possibly non existent page.
				See also RMS-20 edit 673.
				(smw, 11/12/88)

	***** END OF REVISION HISTORY *****



])%



	%([ FORWARD DECLARATIONS ])%

		%(< NONE >)%

	%([ EXTERNAL DECLARATIONS ])%

	EXTERNAL ROUTINE
	    CRASH,		! DEBUGGING
	    FBYRFA,		! FIND A RECORD GIVEN ITS RFA
	    MOVEKEY,	! MOVE A DATA KEY
	    SDATABKT,	! SEARCH A DATA BUCKET
	    GETBKT,		! GET A BUFFER AND MAP A BUCKET
	    PUTBKT,		! RELEASE A BUCKET
	    ALCBKT,		! ALLOCATE A BUCKET
	    DUMPRD,		! DUMP A RECORD DESCRIPTOR
	    DUMPHEADER,	! DUMP A BUCKET HEADER
	    ALCRFA,		! ALLOCATE AN RFA IN A BUCKET
	    SHUFFLEIDS,	! FIND A HOLE OF ID'S IN A BUCKET
	    DUMP;		! SAME


	%([ ERROR MESSAGES REFERENCED IN THIS MODULE ])%

	EXTERNAL
	    MSGINPUT,	! BAD INPUT VALUES
	    MSGCOUNT,	! BAD COUNTER VALUE
	    MSGFAILURE,	! ROUTINE FAILED
	    MSGKDB,		! BAD KDB VALUES
	    MSGRRV,		! RRV FOUND IN SIDR BUCKET
	    MSGDUP,		! DUPLICATE FOUND IN PRIMARY INDEX
	    MSGPTR,		! SOMETHING WRONG IN PTR
!	    MSGUFPGS,	! CANT UPDATE FILE PAGES
	    MSGSPLIT,	! BAD SPLIT VALUES
	    MSGCCR;		! CAN'T CREATE AN RRV (NO ROOM IN BKT )




REQUIRE 'RMSREQ';
EXTDECLARATIONS;



! SPLIT
! =====

! ROUTINE TO SPLIT A DATA BUCKET ON RECORD INSERTION.
!	THIS ROUTINE WILL NOT DO THE ACTUAL RECORD INSERTION
!	BUT WILL DO THE MODIFICATION OF ALL BUCKET OVERHEAD.
!	THIS ROUTINE WILL ALSO NOT DO ANY INDEX
!	MODIFICATION OR RRV UPDATE OF ANY KIND.
!
!	WHEN THE ORIGINAL BUCKET IS SPLIT, ROOM FOR THE NEW
!	RECORD WILL BE LEFT IN THE CORRECT LOCATION FOR THE
!	INSERTION. THUS, THE "HOLE" WILL BE BUILT FOR THE
!	NEW RECORD. ALSO, AN RFA WILL BE ALLOCATED FOR THE
!	RECORD UNLESS THE SIZE OF THE HOLE IS ZERO (THIS IS
!	TRUE FOR A FULL SIDR BUCKET WHICH SPLITS SO A NEW
!	RECORD POINTER IS TO BE ADDED).

! INPUT:
!	RECDESC		RECORD DESCRIPTOR PACKET
!		RECPTR		ADDRESS TO INSERT NEW RECORD
!		LASTRECPTR	ADDRESS OF LAST RECORD IN R-LOW
!		LENGTH		SIZE OF HOLE TO LEAVE FOR NEW RECORD
!				(INCLUDING RECORD HEADER)
!
!	BKTDESC		BKT DESCRIPTOR OF DATA BUCKET TO SPLIT
!	SPLITBD1	BKT DESCRIPTOR OF NEW BUCKET (RETURNED)
!	SPLITBD2	BKT DESCRIPTOR OF 2ND NEW BUCKET (RETURNED)

! OUTPUT:
!	TRUE:	EVERYTHING WAS OK AND BUCKET IS SPLIT
!	FALSE:	ERROR
!		NO MORE FREE PAGES
!		FILE FULL
!
! NOTE:
!	1.	USRSTS WILL CONTAIN THE ERROR CODE ON ERROR RETURN.
!
!	2.	IF AN ERROR RETURN IS TAKEN, NO RECORDS HAVE BEEN
!		MOVED AROUND. THE CALLER SHOULD UNLOCK THE ORIGINAL
!		DATA BUCKET BUT NOT WRITE IT OUT TO THE FILE.
!
!	3.	IF RDLENGTH = 0 ON INPUT, THEN WE ARE NOT INSERTING
!		A NEW RECORD (EITHER PRIMARY OR SECONDARY). INSTEAD,
!		WE ARE SPLITTING A SIDR BUCKET IN ORDER TO ADD ONE
!		RECORD-POINTER ONTO THE END OF AN EXISTING SIDR ARRAY.
!

! ARGUMENTS RETURNED TO CALLER:
!	RECORD DESCRIPTOR:
!		RECPTR		ADDRESS WHERE RECORD IS TO GO
!		LASTRECPTR	NEW HIGH KEY DATA RECORD IN ORIGINAL BUCKET
!				(IF NEW HIGH KEY RECORD IS BEYOND R-NEW)
!		RFA		RFA OF NEW RECORD
!		STATUS
!				FLGIDXUPDATE IS SET (UNLESS DUP SEEN)
!				FLGNOHIKEY IS SET IF THE ORIGINAL BKT IS
!				NOW FILLED ONLY WITH RRV'S.

! ROUTINES CALLED:
!	ALCBKT
!	GETBKT
!

! TERMINOLOGY USED IN THIS ROUTINE:
!
!	R-NEW		THE RECORD TO BE INSERTED
!	R-LOW		THE SET OF RECORDS WITH KEYS .LSS. R-NEW
!	R-HIGH		THE SET OF RECORDS WITH KEYS .GTR. R-NEW
!	S-RRV		SIZE OF CURRENT RRV'S IN BUCKET
!	S-NRRV		SIZE REQUIRED FOR NEW RRV'S WHICH MUST BE
!			CREATED FOR THE RECORDS WHICH ARE GOING TO MOVE.

! THE BASIC ALGORITHM USED BY THIS ROUTINE IS AS FOLLOWS:
!
!	1 )	IF R-NEW WILL FIT IN THE ORIGINAL BUCKET WITH R-LOW,
!		, AS MUCH OF R-HIGH AS POSSIBLE IS
!		KEPT IN ORIGINAL BUCKET AND THE REST IS MOVED OUT.
!		HOWEVER, IF A PRIMARY DATA BUCKET IS BEING SPLIT, AND
!		DUPS ARE ALLOWED, THEN WE MUST INSURE THAT A SERIES
!		OF DUP RECORDS IS NOT SPLIT UP ACROSS BUCKETS. THEREFORE,
!		IN THIS CASE, IF R-NEW WILL FIT WITH R-LOW, THEN ALL
!		OF R-HIGH IS MOVED OUT TO THE NEW BUCKET.
!
!	2 )	IF R-NEW WON'T FIT, IT IS MOVED INTO SPLIT-BKT #1 IF
!		IT WILL FIT WITH R-HIGH. IN THIS CASE, THE NEW RECORD WILL
!		BE POSITIONED AT THE TOP OF THE NEW BUCKET. HOWEVER, IF A
!		PRIMARY BUCKET IS BEING SPLIT AND DUPS ARE ALLOWED, THEN
!		R-NEW MUST GET ITS OWN BUCKET SO AS TO NOT MIX A DUP RECORD
!		WITH OTHER RECORDS.


!
!	3 )	IF R-NEW WON'T FIT WITH EITHER R-LOW OR R-HIGH, OR IF
!		A PRIMARY BUCKET WITH DUPS ALLOWED IS BEING SPLIT,  R-NEW IS
!		MOVED INTO SPLIT-BKT #2 BY ITSELF. NOTE THAT THIS SITUATION IS
!		VERY RARE--IT IS POSSIBLE ONLY IF THE NEW RECORD
!		IS VERY LARGE (BECAUSE IF IT WEREN'T VERY LARGE, IT
!		WOULD BE ABLE TO FIT IN A SINGLE BUCKET WITH
!		EITHER R-LOW OR R-HIGH).


GLOBAL ROUTINE SPLIT ( RECDESC, BKTDESC, SPLITBD1, SPLITBD2 ) =
BEGIN

	ARGUMENT	(RECDESC,BASEADD);		! RECORD DESCRIPTOR
	ARGUMENT	(BKTDESC,BASEADD);		! ORIGINAL BUCKET
	ARGUMENT	(SPLITBD1,BASEADD);		! 1ST NEW BKT
	ARGUMENT	(SPLITBD2,BASEADD);		! 2ND NEW BKT

LOCAL
    OLDBKTPTR,		! PTR TO ORIGINAL BUCKET
    OLDBUCKET,		! BUCKET # OF ORIGINAL BUCKET
    OLDMOVINGPTR,		! TEMP PTR TO SAME
    TEMPPTR:	POINTER,	! TEMPORARY POINTER
    SPLIT1BKT,		! BKT # OF FIRST NEW BUCKET
    SPLIT1BKTPTR,		! PTR TO SAME
    SPLIT1MOVINGPTR,	! TEMP PTR TO SAME
    SPLIT2BKT,		! BKT # OF 2ND NEW BUCKET
    SPLIT2BKTPTR,		! PTR TO SECOND NEW BUCKET
    OLDRRVPTR,		! PTR TO START OF OLD RRV'S
    OLDENDPTR,		! PTR TO END OF ORIGINAL BUCKET
    INSERTPTR,		! PTR TO PLACE TO INSERT NEW RECORD
    RHIGHPTR,		! PTR TO START OF R-HIGH
    SIZERRV,		! SIZE OF OLD RRV'S
    SIZENEWRRV,		! SIZE OF NEW RRV'S WHICH HAVE TO BE CREATED
    SIZELOW,		! SIZE OF R-LOW
    SIZEHIGH,		! SIZE OF R-HIGH
    UDRFLAG,		! ON IF THIS IS A UDR BUCKET
    SUM,			! TEMP
    BKTSIZE,		! SIZE OF DATA BUCKET
    SIZEOFLASTRECRD,	! SIZE OF THE LAST RECORD WE SCANNED
    SIZEOFTHISRECRD,	! SIZE OF CURRENT RECORD
    MAXDATASIZE,		! AMOUNT OF SPACE LEFT IN BUCKET
    MAXAVAILABLE,		! AMOUNT OF SPACE WHICH CAN BE USED (DIFFERS
				! FROM MAXDATASIZE ONLY IF FILL PERCENTS ARE USED
    SIZEOFNEWRECORD,	! GUESS
    SHIGHINOLD,		! AMOUNT OF R-HIGH TO STAY IN OLD BKT
    SHIGHINNEW,		! AMOUNT OF R-HIGH TO GO TO NEW BKT
    SNEWINOLD,		! IF NON-ZERO, R-NEW STAYS IN ORIGINAL BKT
    SNEWINNEW,		! IF NON-ZERO, R-NEW GOES TO NEW BKT
    TWOBUCKETSPLIT,		! FLAG: TRUE=2-BKT SPLIT, FALSE=3-BKT SPLIT
    LASTRHIGHOLDPTR:	POINTER,	! PTR TO LAST RECORD IN R-HIGH TO STAY
    MAXBKTOFFSET,		! FILL PERCENT OFFSET FOR DATA BUCKET
    NEWRECBD:	VECTOR[ BDSIZE ];	! BKT DESC FOR NEW RECORD BUCKET

MAP
    NEWRECBD:	FORMAT;

REGISTER
    TEMPAC;

MAP
    RECDESC:	POINTER,
    BKTDESC:	POINTER,
    SPLITBD1:	POINTER,
    SPLITBD2:	POINTER,
    OLDMOVINGPTR:	POINTER;
MAP
    SPLIT1BKTPTR:	POINTER,
    SPLIT2BKTPTR:	POINTER,
    SPLIT1MOVINGPTR:	POINTER,
    OLDRRVPTR:	POINTER,
    OLDENDPTR:	POINTER;
MAP
    INSERTPTR:	POINTER,
    RHIGHPTR:	POINTER,
    LASTRHIGHOLDPTR:	POINTER;
MAP
    OLDBKTPTR:	POINTER;
LABEL SCANRECS,SIZELOOP;



	TRACE ('SPLIT');
	CHECKEXACTCOUNT;

	%([ LET'S LOOK AT THE RECORD DESCRIPTOR ON INPUT ])%

	%IF DBUG %THEN
	TYPE (%STRING('RECORD DESC. ON INPUT TO SPLIT:'));
	CALLDUMPRD ( BPT ( RECDESC ) );
	%FI


	%([ PICK UP SOME POINTERS AND CLEAR SOME VARIABLES ])%

	UDRFLAG = 1;					! ASSUME ITS UDR'S
	IF .KDB [ KDBREF ] ISNT REFPRIMARY THEN UDRFLAG = FALSE;
	RHIGHPTR = ( INSERTPTR = .RECDESC [ RDRECPTR ] );	! GET PLACE FOR NEW RECORD
	OLDBUCKET = .BKTDESC [ BKDBKTNO ];
	OLDBKTPTR = .BKTDESC [ BKDBKTADR ];
	OLDENDPTR = .OLDBKTPTR + .OLDBKTPTR [ BHNEXTBYTE ];
	BKTSIZE = .KDB [ KDBDBKZ ];			! GET BUCKE SIZE
	MAXDATASIZE = ( .BKTSIZE ^ B2W ) - BHHDRSIZE;	! COMPUTE MAX SPACE
	MAXAVAILABLE = .MAXDATASIZE;			! ASSUME SAME
	IF ( CHKFLAG ( RAB [ RABROP ], ROPLOA ) ISON )
	THEN
		MAXAVAILABLE = .KDB [ KDBDFLOFFSET ] - BHHDRSIZE;
	LOOKAT ('	MAX-DATA-SIZE: ', MAXDATASIZE);
	LOOKAT ('	MAX-AVAIL: ', MAXAVAILABLE );

	%([ ASSUME THAT R-NEW AND R-HIGH WILL BE MOVED, SO
	   SET UP THE PTR TO THE LAST RECORD IN THE BUCKET ])%

	LASTRHIGHOLDPTR = .RECDESC [ RDLASTRECPTR ];

	%([ CLEAR ALL OUR SPLIT-DESCRIPTOR VALUES ])%

	SHIGHINOLD = ( SHIGHINNEW = ZERO );
	SNEWINOLD = ( SNEWINNEW = ZERO );
	TWOBUCKETSPLIT = TRUE;				! ASSUME SIMPLE

	%([ SET THE FLAG TO INDICATE THAT AN INDEX UPDATE IS REQUIRED ])%

	SETIDXUPDATFLAG ( RECDESC );

	%([ INDICATE THAT IT WAS A SIMPLE (2-BUCKET) SPLIT ])%

	RECDESC [ RDCOUNT ] = 1;

	%([ FIND THE SIZE OF ALL RECORDS IN R-LOW ])%

	SIZELOW = .INSERTPTR - .OLDBKTPTR - BHHDRSIZE;
	LOOKAT ('	SIZE OF R-LOW: ', SIZELOW );

	%([ WE MUST NOW PASS OVER R-HIGH AND COMPUTE VARIOUS SIZES ])%

	SIZENEWRRV = ( SIZERRV = ZERO );
	SIZEHIGH = ZERO ;

	%([ INIT A SCANNING POINTER ])%

	OLDMOVINGPTR = .INSERTPTR;

	%([ SET THE RRV POINTER TO THE END OF BUCKET. ACTUALLY,
	   THIS IS DONE TO AVOID THE BUG HALT IF THERE ARE NO
	   RRV RECORDS IN THE BUCKET ])%

	OLDRRVPTR = .OLDENDPTR;

	%([ LOOP OVER ALL RECORDS FROM HERE TO BOTTOM OF BUCKET
	   AND COMPUTE SEVERAL VARIOUS VALUES  ])%

SIZELOOP:BEGIN

	UNTIL .OLDMOVINGPTR GEQ .OLDENDPTR  DO

		BEGIN

		%([ IF THIS IS A SIDR BUCKET, THEN WE DONT HAVE TO
		   SCAN IT BECAUSE THERE WILL NOT BE ANY RRV'S.
		   THEREFORE, WE CAN COMPUTE DIRECTLY THE SIZE OF
		   R-HIGH AND R-LOW ])%

		IF .UDRFLAG IS FALSE
		THEN
			BEGIN
			SIZEHIGH = .OLDENDPTR - .OLDMOVINGPTR;
			LEAVE SIZELOOP		! EXIT FROM LOOP
			END; %(OF IF THIS IS A SIDR BUCKET)%

		%([ CHECK TO SEE IF WE HAVE REACHED THE RRV'S ])%

		IF RRVFLAG ( OLDMOVINGPTR ) IS OFF
		THEN %(THIS IS STILL A DATA RECORD)%

			BEGIN
			SIZEOFTHISRECRD = SIZEOFUDR (  OLDMOVINGPTR );
			SIZEHIGH = .SIZEHIGH + .SIZEOFTHISRECRD;

			%([ CHECK IF WE NEED AN RRV FOR THIS RECORD ])%

			IF .OLDMOVINGPTR [ DRRRVBUCKET ] IS .OLDBUCKET
			THEN %(WE WILL NEED AN RRV)%
				BEGIN
				LOOKAT ('	REC NEEDS RRV AT: ', OLDMOVINGPTR );
				INC ( SIZENEWRRV, RRVRECSIZE )
				END %(OF WE NEED AN RRV)%
			ELSE
				LOOKAT ('	REC DOESNT NEED RRV AT: ', OLDMOVINGPTR );

			%([ BUMP THE POINTER TO NEXT RECORD ])%

			INC ( OLDMOVINGPTR, .SIZEOFTHISRECRD )
			END %(OF IF THIS IS STILL A DATA RECORD)%

		ELSE	%(THIS IS AN RRV (THE FIRST ONE))%

			BEGIN
			IF .UDRFLAG IS FALSE THEN RMSBUG ( MSGRRV );
			%([ REMEMBER WHERE FIRST RRV WAS ])%

			OLDRRVPTR = .OLDMOVINGPTR;

			%([ COMPUTE SIZE OF ALL CURRENT RRV'S ])%

			SIZERRV = .OLDENDPTR - .OLDMOVINGPTR;
			LOOKAT ('	1ST RRV AT: ',OLDRRVPTR );
			LOOKAT ('	SIZE OF RRVS: ',SIZERRV);
			LEAVE SIZELOOP;
			END %(OF ELSE THIS IS AN RRV)%

		END; %(OF WHILE LOOP)%
	END;  %(OF SIZELOOP)%



	%([ WE HAVE NOW SCANNED ALL OF R-HIGH AND COMPUTED THE SIZE
	   OF R-HIGH AND ALL THE RRV'S IN THE BUCKET. WE NEED TO NOW
	   FIGURE OUT HOW MUCH SPACE WE ABSOLUTELY REQUIRE IN THIS
	   BUCKET SO WE CAN THEN DETERMINE HOW MUCH DATA WE CAN MOVE OUT. ])%

	SUM = .SIZELOW + .SIZERRV + .SIZENEWRRV;
	LOOKAT ('	SUM: ', SUM );

	%([ "SUM" NOW REPRESENTS THE MINIMUM AMOUNT WHICH MUST REMAIN
	   IN THIS BUCKET IF WE SPLIT AT THE CURRENT RECORD ])%

	%([ CHECK TO SEE IF NEW RECORD CAN FIT HERE ])%

	SIZEOFNEWRECORD = .RECDESC [ RDLENGTH ] ;
	LOOKAT ('	SIZE OF HOLE TO LEAVE: ',SIZEOFNEWRECORD);

	%([ CHECK TO SEE THAT SPLIT SHOULD HAVE BEEN CALLED ])%

	IF .SIZEOFNEWRECORD GTR .MAXDATASIZE THEN RMSBUG ( MSGINPUT );

	%([ CAN R-NEW FIT? ])%

	IF ( .SUM + .SIZEOFNEWRECORD ) LEQ .MAXAVAILABLE
	THEN %(THE NEW RECORD WILL FIT IN THIS BUCKET)%

		BEGIN

		RTRACE (%STRING('	R-NEW WILL FIT IN THIS BKT'));

		%([ MAYBE SOME RECORDS IN R-HIGH WILL FIT ALSO ])%

		RTRACE (%STRING('	SCANNING RHIGH...'));
		OLDMOVINGPTR = .INSERTPTR;		! GET TEMP PTR

		%([ DETERMINE THE POINT AT WHICH WE WANT TO CONSIDER
		   THE BUCKET AS BEING FULL ])%

		MAXBKTOFFSET = .OLDBKTPTR [ BHNEXTBYTE ] ^ DIVIDEBY2LSH;

		%([ LET'S LOOK AT THE MAX BUCKET OFFSET ])%

		LOOKAT ('	MAXBKTOFFSET: ', MAXBKTOFFSET );

!** [15] ROUTINE:SPLIT AT LINE 7370, EGM, 26-JUL-78
%([15])%		%([ SETUP THE SIZE OF THE PREVIOUS RECORD ( THE ONE )%
%([15])%		%(  POINTED TO BY LASTRHIGHOLDPTR), TO EITHER: 0 )%
%([15])%		%(  FOR THE FIRST RECORD IN THE BUCKET, OR TO THE SIZE )%
%([15])%		%(  OF THE PREVIOUS RECORD, SO THAT IF THE VERY FIRST )%
%([15])%		%(  RECORD WE SEE PUTS US OVER THE LIMIT, LAST-RHIGH-PTR )%
%([15])%		%(  WILL STILL BE CORRECT ])%
%([15])%
%([15])%		IF .RECDESC [ RDRECPTR ] EQL .RECDESC [ RDLASTRECPTR ]
%([15])%		THEN
%([15])%			SIZEOFTHISRECRD = ZERO
%([15])%		ELSE
%([15])%			SIZEOFTHISRECRD = SIZEOFDATARECRD ( LASTRHIGHOLDPTR );

		%([ LOOP UNTIL THE BUCKET MEETS THIS FILL CRITERION ])%
SCANRECS: BEGIN

	UNTIL ( .SUM + .SIZEOFNEWRECORD )
				GEQ
			( .MAXBKTOFFSET )
		DO %(THIS LOOP)%
			BEGIN

			%([ REMEMBER THE SIZE OF THE LAST RECORD. THIS IS
			   BECAUSE IF THE LAST RECORD WE SCAN IS TOO BIG,
			   WE MUST REMEMBER THE SIZE OF THE RECORD BEFORE IT ])%

!** [12] ROUTINE:SPLIT AT LINE 7388, EGM, 5-APR-78
%([12])%			SIZEOFLASTRECRD = .SIZEOFTHISRECRD;

			%([ IF WE ARE SPLITTING A PRIMARY DATA BUCKET, AND IF
			   DUPS ARE ALLOWED, THEN WE MUST INSURE THAT
			   THE SPLIT OCCURS EITHER BEFORE OR AFTER THE NEW
			   RECORD. I.E., WE DON'T WANT TO DISTRIBUTE THE
			   OTHER DUPS ACROSS BUCKETS WITH OTHER RECORDS.
			   THEREFORE, WE WILL CHECK HERE AND IF THESE CONDITIONS HOLD,
			   WE WILL PUT THE NEW RECORD ON THIS BUCKET AND
			   MOVE EVERYTHING ELSE OUT. ])%

			IF ( .UDRFLAG ISNT FALSE )	! PRIMARY DATA RECORDS
			THEN IF DUPLICATES		! AND DUPS ALLOWED
			THEN
				LEAVE SCANRECS;		! THEN EXIT NOW

			%([ CONSISTENCY CHECK ])%

			IF .OLDMOVINGPTR GEQ .OLDENDPTR THEN rmsbug (MSGPTR);
			SIZEOFTHISRECRD = SIZEOFDATARECRD ( OLDMOVINGPTR );
			INC ( SUM, .SIZEOFTHISRECRD );

			%([ HOWEVER, IF WE ORIGINALLY CALCULATED THAT THIS
			   RECORD WAS TO BE MOVED, WE MUST SUBTRACE THE
			   SIZE OF THE RRV WHICH WE INCLUDED IN "SUM" ])%

			IF .UDRFLAG ISNT FALSE
			THEN IF .OLDMOVINGPTR [ DRRRVBUCKET ] IS .OLDBUCKET
			THEN
				DEC ( SUM, RRVRECSIZE );
			LOOKAT ('	UPDATE SUM VALUE: ',SUM);
			INC ( OLDMOVINGPTR, .SIZEOFTHISRECRD );
			LOOKAT ('	PTR BUMPED TO: ', OLDMOVINGPTR )
			END; %(OF UNTIL BUCKET IS FULL)%
		END; %( OF SCANRECS LOOP )%


		%([ HOWEVER, WE MAY HAVE ALSO GONE OVER A FULL BUCKET
		   IF THE LAST RECORD WAS A REALLY BIG ONE. SO, CHECK
		   IF THIS LAST RECORD HAS PUT US OVER A FULL BUCKET ])%

!** [15] ROUTINE:SPLIT, AT LINE 7428, EGM, 26-JUL-78
%([15])%		IF ( .SUM + .SIZEOFNEWRECORD ) GTR .MAXAVAILABLE %(UDR)%
%([15])%				OR
%([15])%		     .SUM EQL .MAXAVAILABLE	%(SIDR)%
		THEN	%(WE MUST BACK UP ONE DATA RECORD)%
			BEGIN
			RTRACE (%STRING('	LAST RECORD TOO BIG'));
			DEC ( SUM, .SIZEOFTHISRECRD );
			DEC ( OLDMOVINGPTR, .SIZEOFTHISRECRD );
			SIZEOFTHISRECRD = .SIZEOFLASTRECRD	! ADJUST LAST RECORD SIZE
			END; %(OF WE MUST BACK UP ONE RECORD)%

		%([ WE NOW HAVE GONE OVER HALF A FULL BUCKET. ])%

		LASTRHIGHOLDPTR = .OLDMOVINGPTR - .SIZEOFTHISRECRD;
		LOOKAT ('	LAST R-HIGH IN OLD: ', LASTRHIGHOLDPTR );
		%([ WE NOW KNOW HOW TO SPLIT THE BUCKET ])%

		SNEWINOLD = .SIZEOFNEWRECORD;		! R-NEW GOES HERE
		SHIGHINOLD = .OLDMOVINGPTR - .INSERTPTR;
		SHIGHINNEW = .SIZEHIGH - .SHIGHINOLD;	! THIS MUCH GOES
		END %(OF IF SUM + SIZEOFNEWRECORD LEQ MAXSIZE)%

	ELSE	%(THE NEW RECORD CANNOT FIT IN THIS BUCKET)%

		BEGIN

		%([ EITHER IT WILL FIT ENTIRELY IN WITH R-HIGH, OR IT
		   MUST GO IN ITS OWN BUCKET ])%

		SHIGHINNEW = .SIZEHIGH;			! ALL OF R-HGH MOVES

		%([ IF THE NEW RECORD PLUS THE SIZE OF R-HIGH IS TOO
		   BIG FOR THE BUCKET, OR IF THE NEW RECORD IS A DUPLICATE,
		   THEN WE NEED TO HAVE A THREE BUCKET SPLIT. IN THE
		   LATTER CASE, WE WON'T ENTER THE BUCKET CONTAINING
		   THE NEW RECORD INTO THE INDEX BECAUSE IT IS MERELY
		   AN EXTENSION OF THE ORIGINAL BUCKET CONTAINING THE
		   OTHER DUPLICATES. ])%

		IF ( ( ( .SIZEOFNEWRECORD + .SIZEHIGH ) GTR .MAXDATASIZE )
				OR
		   ( DUPLICATEFLAG ( RECDESC ) ISON ) AND ( .SIZEHIGH ISNT ZERO ) )

		THEN %(THIS IS A 3-BKT SPLIT)%

			BEGIN
			RTRACE (%STRING('*******A 3-BKT SPLIT'));
			RECDESC [ RDCOUNT ] = 2;	! SIGNAL IT
			TWOBUCKETSPLIT = FALSE;		! REMEMBER THAT
			END

		ELSE	%(THIS IS A NORMAL 2-BKT SPLIT)%

			BEGIN
			RTRACE (%STRING('	R-NEW WILL GO INTO NEW BKT'));

			%([ IF R-NEW WILL GO INTO A BUCKET BY ITSELF (I.E,
			   IF S-HIGH=0), AND A DUPLICATE HAS BEEN SEEN, THEN
			   WE DONT WANT TO UPDATE THE INDEX BECAUSE THIS
			   DUPLICATE RECORD (R-NEW) WILL GO INTO A HORIZONTAL
			   BUCKET BY ITSELF ])%

			IF ( DUPLICATEFLAG ( RECDESC ) ISON )
			THEN CLRFLAG ( RECDESC [ RDSTATUS ], RDFLGIDXUPDATE );

			SNEWINNEW = .SIZEOFNEWRECORD;
			END %(OF ELSE R-NEW GOES IN WITH R-HIGH)%

		END; %(OF ELSE WE MUST MOVE THE NEW RECORD)%

	%([ LET'S SEE EVERYTHING ])%

	%IF DBUG %THEN
	TYPE (%STRING('***SPLIT DESCRIPTOR:'));
	PRINTVALUE ('	S-NEW-IN-OLD: ',SNEWINOLD);
	PRINTVALUE ('	S-NEW-IN-NEW: ',SNEWINNEW);
	PRINTVALUE ('	S-HIGH TO STAY: ', SHIGHINOLD );
	PRINTVALUE ('	S-HIGH TO MOVE: ', SHIGHINNEW );
	PRINTVALUE('	2-BKT FLAG: ', TWOBUCKETSPLIT);
	PRINTVALUE ('	SIZE OF RRVS: ', SIZERRV );
	%FI

	%([ CHECK OUT ALL THESE VALUES TO SEE IF THEY ARE REASONABLE ])%

	IF	( ( .INSERTPTR + .SHIGHINOLD + .SHIGHINNEW + .SIZERRV )
			ISNT .OLDENDPTR )
			OR
		(  ( .OLDRRVPTR + .SIZERRV ) ISNT .OLDENDPTR )
			OR
		( .SIZEHIGH ISNT ( .SHIGHINOLD + .SHIGHINNEW ) )
	THEN
		RMSBUG ( MSGSPLIT );


	%([ HERE IS A BRIEF SUMMARY OF SOME OF OUR CURRENT VALUES:

		INSERTPTR	PLACE IN OLD BUCKET TO PUT RECORD
		OLDRRVPTR	PTR TO START OF RRV'S
		OLDENDPTR	PTR TO END OF OLD BUCKET
	 ])%

	%([ ALLOCATE A FIRST NEW BUCKET ])%

	IF CALLALCBKT (%(TYPE)%		PCI ( BTYPEDATA),
			%(FLAGS)%	PCI ( ZERO ),
			%(LEVEL)%	PCI ( DATALEVEL ),
			%(BD)%		BPT ( SPLITBD1 )) IS FALSE

	THEN
		RETURN FALSE;

	%([ GET THE BUCKET NUMBER )%

	SPLIT1BKT = .SPLITBD1 [ BKDBKTNO ];
	SPLIT1BKTPTR = .SPLITBD1 [ BKDBKTADR ];
	LOOKAT ('	NEW BKT IS AT: ', SPLIT1BKTPTR );

	%([ LINK ALL THESE BUCKETS TOGETHER ])%

	SPLIT1BKTPTR [ BHNEXTBKT ] = .OLDBKTPTR [ BHNEXTBKT ];
	OLDBKTPTR [ BHNEXTBKT ] = .SPLIT1BKT;
	SPLIT1BKTPTR [ BHFLAGS ] = .OLDBKTPTR [ BHFLAGS ] AND BHFLGEND;

	%([ CLEAR THE FLAGS IN THE ORIGINAL DATA BUCKET (FLGEND BIT) ])%

	CLRFLAG ( OLDBKTPTR [ BHFLAGS ], BHFLGEND );


	%([ REMEMBER WHICH BUCKET R-NEW WILL GO INTO )%

	MOVEBKTDESC ( %(FROM)% SPLITBD1, %(TO)% NEWRECBD );

	%([ NOW, LET'S ALLOCATE ANOTHER BUCKET IF A 3-BKT SPLIT ])%

	IF .TWOBUCKETSPLIT IS FALSE
	THEN %(WE NEED ANOTHER BUCKET)%

		BEGIN
		RTRACE (%STRING('	ALLOCATING ANOTHER BKT...'));
		IF CALLALCBKT (%(TYPE)%		PCI ( BTYPEDATA ),
				%(FLAGS)%	PCI ( ZERO ),
				%(LEVEL)%	PCI ( DATALEVEL),
				%(BD)%		BPT ( SPLITBD2 ) ) IS FALSE
		THEN
			BEGIN
			RTRACE (%STRING('	ALCBKT FAILED**'));

			%([ WE MUST NOW GIVE BACK THE BUCKET THAT WE JUST GOT.
			   FOR NOW, THE "DEALLOCATING" OF A FILE BUCKET CANNOT
			   BE DONE. IN THE FUTURE, IT WILL BE PUT ONTO A LINKED
			   LIST OF SPARE BUCKETS ])%

			DEALLOCBUCKET (	%(BUCKET)%	SPLITBD1,
						%(SIZE)%		.BKTSIZE );
			BADRETURN;
			END; %(OF CANT GET A SECOND BUCKET)%

		%([ REMEMBER THAT R-NEW WILL GO INTO THIS BUCKET ])%

		MOVEBKTDESC ( %(FROM)% SPLITBD2, %(TO)% NEWRECBD );

		%([ SET UP SOME POINTERS TO THE 2ND BUCKET AND FILL IN THE HEADER ])%

		SPLIT2BKTPTR = .SPLITBD2 [ BKDBKTADR ];
		SPLIT2BKTPTR [ BHNEXTBKT ] = .SPLIT1BKT;	! PUT THIS BKT INTO CHAIN
		OLDBKTPTR [ BHNEXTBKT ] = .SPLITBD2 [ BKDBKTNO ];	! MAKE OLD POINT TO THIS ONE
		SPLIT2BKTPTR [ BHNEXTBYTE ] = .SIZEOFNEWRECORD + BHHDRSIZE
		END; %(OF IF 2BUCKETFLAG IS FALSE)%



	%([ WE NOW HAVE ADJUSTED ALL THE HEADER INFO (EXCEPT FOR NEXTBYTE) AND PLACED THE
	   BUCKET FLAGS (ACTUALLY JUST THE "END" FLAG BIT ) INTO THE
	   NEW BUCKET. LET'S SEE EVERYTHING ])%

	%IF DBUG %THEN
	TYPE (%STRING('***DUMP OF SPLIT BKT-HDR: '));
	CALLDUMPHEADER ( LPT ( SPLIT1BKTPTR ) );
	IF .TWOBUCKETSPLIT IS FALSE
	THEN %(WE SHOULD PRINT OUT THE OTHER BUCKET TOO)%
		BEGIN
		TYPE (%STRING('***DUMP OF 2ND BKT HEADER:'));
		CALLDUMPHEADER ( LPT ( SPLIT2BKTPTR ) );
		END;
	%FI

	%([ DOES R-NEW GO IN THE ORIGINAL BUCKET? ])%

	IF .SNEWINOLD ISNT ZERO
	THEN %(R-NEW GOES IN ORIGINAL BUCKET)%

		BEGIN

		%([ REMEMBER WHICH BUCKET CONTAINS R-NEW ])%

		MOVEBKTDESC ( %(FROM)% BKTDESC, %(TO)% NEWRECBD );

		%([ SOME (OR ALL) OF R-HIGH MUST BE MOVED OUT ])%

		RTRACE (%STRING('	MOVING R-HIGH TO NEW BKT...'));
		IF .SHIGHINNEW IS ZERO THEN RMSBUG ( MSGSPLIT );
		MOVEWORDS (	%(FROM)%	.INSERTPTR + .SHIGHINOLD,
				%(TO)%		.SPLIT1BKTPTR + BHHDRSIZE,
				%(SIZE)%	.SHIGHINNEW );

		%([ CHECK TO SEE IF WE NEED TO MOVE THE RRV'S DOWN OR
		   UP IN THE BUCKET. THEY WILL BE MOVED UP IF THE RECORDS
		   IN R-HIGH WE ARE MOVING OUT ARE BIGGER THAN R-NEW.
		   THEY WILL BE MOVED DOWN IF THE OPPOSITE IS TRUE ])%

		IF .SIZERRV ISNT ZERO
		THEN %(RRV'S MUST BE MOVED)%

			BEGIN

			%([ UP? ])%

			IF .SHIGHINNEW GTR .SIZEOFNEWRECORD
			THEN %(RRV'S SHOULD GO UP)%

				BEGIN
				RTRACE (%STRING('	MOVING RRVS UP...'));
				MOVEWORDS (	%(FROM)%	.OLDRRVPTR,
						%(TO)%	.INSERTPTR+.SIZEOFNEWRECORD
							+ .SHIGHINOLD,
						%(SIZE)%	.SIZERRV );
				END %(OF RRV'S GO UP)%

			ELSE	%(THEY GO DOWN)%

				BEGIN
				RTRACE (%STRING('	MOVING RRVS DOWN...'));
				MOVEDOWN (	%(START)%	.OLDRRVPTR,
						%(END)%	.OLDENDPTR - 1,
						%(SIZE)%	.SIZEOFNEWRECORD
							- .SHIGHINNEW );
				END %(OF MOVING RRV'S DOWN)%

			END; %(OF IF SIZERRV ISNT ZERO)%

		%([ NOW, MOVE R-HIGH DOWN ])%

		IF .SHIGHINOLD ISNT ZERO
		THEN

			BEGIN
			RTRACE (%STRING('	MOVING R-HIGH DOWN...'));
			IF .SIZEOFNEWRECORD ISNT ZERO %(COULD BE A NULL SIDR)%
			THEN
				MOVEDOWN (	%(FROM)%	.INSERTPTR,
						%(TO)%	.INSERTPTR + .SHIGHINOLD -1,
						%(SIZE)%	.SIZEOFNEWRECORD );

			%([ RESET THE PTR TO NEW LAST RECORD IN BUCKET ])%

			INC ( LASTRHIGHOLDPTR ,.SIZEOFNEWRECORD);
			END %(OF IF SHIGHINOLD ISNT ZERO)%

		ELSE	%(ALL OF R-HIGH GETS MOVED OUT)%

			LASTRHIGHOLDPTR = .INSERTPTR
		END %(OF IF R-NEW GOES IN ORIGINAL BUCKET)%

	ELSE	%(R-NEW WILL BE MOVED)%

		BEGIN

		%([ DOES R-NEW GO INTO IT'S OWN BUCKET? ])%

		IF .TWOBUCKETSPLIT
		THEN
			INSERTPTR = .SPLIT1BKTPTR + BHHDRSIZE
		ELSE
			%(IT'S A 3-BKT SPLIT)%
			BEGIN
			INSERTPTR = .SPLIT2BKTPTR + BHHDRSIZE;
			SNEWINNEW = ZERO
			END; %(OF ELSE A 3-BKT SPLIT)%

		%([ NOW, MOVE ALL OF R-HIGH OUT ])%
		IF .SHIGHINNEW ISNT ZERO
		THEN
			BEGIN
			%([ NOTE THAT THE SOURCE ADDRESS OF THIS OPERATION
			   IS THE START OF R-HIGH PLUS THE SIZE OF R-HIGH
			   WHICH WILL STAY IN THIS BUCKET. FOR USER DATA
			   RECORDS, THIS INCREMENT IS ALWAYS ZERO. FOR
			   SIDR RECORDS, IT MAY BE NON-NULL IF WE ARE
			   ONLY SPLITTING THE BUCKET WITHOUT INSERTING
			   A NEW RECORD (I.E., ADDING A PTR TO AN ARRAY) ])%

			MOVEWORDS (	%(FROM)%	.RHIGHPTR + .SHIGHINOLD,
					%(TO)%	.SPLIT1BKTPTR + BHHDRSIZE + .SNEWINNEW,
					%(SIZE)%	.SHIGHINNEW );

			%([ NOW, MOVE RRV'S UP IN THE BUCKET ])%

			IF .SIZERRV ISNT ZERO
			THEN
				MOVEWORDS (	%(FROM)%	.OLDRRVPTR,
						%(TO)%	.OLDBKTPTR+BHHDRSIZE+.SIZELOW,
						%(SIZE)%	.SIZERRV);
			END %(OF IF SHIGHINNEW ISNT ZERO)%

		END; %(OF R-NEW GOES INTO SEPARATE BUCKET)%



%([** FIX. REMEMBER THAT R-NEW GOES INTO NEW BUCKET ON SEQ ACCESS. THIS  **])%
%([** CONDITION IS INDICATED BY THE FLAG FLGNEWINNEW IN RECDESC.	 **])%

	IF (SEQADR AND .TWOBUCKETSPLIT AND ( .SNEWINNEW ISNT ZERO ))
	THEN					![%51] PUT . ON SNEWINNEW ABOVE
		SETNEWINNEWFLG ( RECDESC );

%([**			END OF FIX.					 **])%



	%([ NOW, SET UP NEXT-BYTE POINTER FOR BOTH BUCKETS ])%

	OLDBKTPTR [ BHNEXTBYTE ] = .OLDBKTPTR [ BHNEXTBYTE ] - .SHIGHINNEW + .SNEWINOLD;
	SPLIT1BKTPTR [ BHNEXTBYTE ] = BHHDRSIZE + .SNEWINNEW + .SHIGHINNEW;

	%([ THE NEW RECORD CAN NOW BE INSERTED AT "INSERTPTR" ])%

	RECDESC [ RDRECPTR ] = .INSERTPTR;

	%([ ALLOCATE A NEW RFA FOR THIS RECORD, UNLESS THIS IS
	   A BUCKET INTO WHICH A NEW RECORD IS NOT TO BE INSERTED ])%

	IF .SIZEOFNEWRECORD ISNT ZERO
	THEN	%(GET A NEW RFA)%
		RECDESC [ RDRFA ] = CALLALCRFA (	LCT ( NEWRECBD ) );

	%([ SET UP THE PTR TO THE LAST RECORD IN THE ORIGINAL
	   BUCKET, AND FIGURE OUT IF THERE ARE SOME DATA RECORDS
	   IN THAT BUCKET (IT MAY BE FULL OF RRV'S) ])%

	RECDESC [ RDLASTRECPTR ] = .LASTRHIGHOLDPTR;
	IF RRVFLAG ( LASTRHIGHOLDPTR ) ISON
	THEN
		SETFLAG ( RECDESC [ RDSTATUS ], RDFLGNOHIKEY );

	%IF DBUG %THEN
	TYPE (%STRING('***RD AFTER SPLIT...'));
	CALLDUMPRD ( BPT ( RECDESC ) );
	%FI

	GOODRETURN

END; %(OF SPLIT)%



! COMPRESS
! ========

! ROUTINE TO COMPRESS A USER DATA BUCKET WHEN A NEW RECORD WILL
!	NOT FIT. THIS ROUTINE ATTEMPTS TO SQUEEZE THE BUCKET
!	AND REMOVE DELETED RECORDS IN ORDER TO MAKE ROOM FOR THE
!	THE NEW RECORD WHICH IS TO BE INSERTED.
!
!	THIS ROUTINE IS CALLED ONLY ON RECORD INSERTION ($PUT).
!	THE $DELETE OPERATION DOES NO BUCKET COMPRESSION AT ALL
!	BECAUSE THE CURRENT FILE POSITION IS NOT LOCKED WHEN
!	THE $DELETE IS DONE.

! INPUT:
!	RECDESC		RECORD DESCRIPTOR PACKET
!		RECPTR		ADDRESS OF INSERTION POSITION
!		LASTRECPTR	ADDR OF HI REC FOR ORIG BKT, IF SPLIT
!
!	DATABD		BUCKET DESCRIPTOR OF CURRENT BUCKET

! OUTPUT:
!	AMOUNT OF SPACE RECLAIMED BY COMPRESSION

! INPUT ARGS MODIFIED:
!
!	RECORD DESCRIPTOR:
!		RECPTR		NEW INSERTION POINT
!		LASTRECPTR	NEW HI REC FOR SPLIT

! NOTES:
!
!	1.	WHEN THIS ROUTINE IS CALLED, IT IS ASSUMED THAT
!		ALL SECONDARY INDEX RECORDS (SIDR'S) FOR EACH DELETED
!		RECORD HAVE ALREADY BEEN DELETED OR OTHERWISE TAKEN
!		CARE OF. THUS, WE ONLY MUST COMPRESS THE PRIMARY
!		DATA RECORD.
!
!	2.	IF THIS IS A NEW FILE (IT IS CURRENTLY BEING CREATED)
!		OR IF THERE ARE DUPLICATES ALLOWED IN THE PRIMARY INDEX,
!		THEN WE WILL DO NO COMPRESSION AT ALL. THIS ALGORITHM
!		IS NON-OPTIMAL IF $DELETES ARE DONE DURING THE CREATION
!		OF THE FILE, BUT THIS IS ALMOST GUARENTEED TO BE A VERY
!		RARE OCCURANCE AND WE DON'T WANT TO PAY THE OVERHEAD
!		OF ATTEMPTING A COMPRESSION ON A BUCKET WHICH WILL NOT
!		HAVE ANY DELETED RECORDS IN IT. IF DUPLICATES ARE ALLOWED,
!		COMPRESSION IS NOT DONE BECAUSE IT MAY CAUSE THE
!		CURRENT RECORD POSITION OF A USER TO BE LOST.
!
!	3.	THERE ARE TWO PRIMARY OPTIMIZATIONS USED BY THIS ROUTINE:
!			A) CONTIGUOUS DELETED RECORDS ARE COMPRESSED AS A UNIT
!			B) RRV'S ARE COMPRESSED A BUCKET AT A TIME.
!
!	4.	THIS ROUTINE USES A LOCAL DATA STRUCTURE TO SPEED UP
!		THE PROCESSING OF THE DELETED RECORDS (CALLED THE
!		"DELETETABLE"). IT HAS TWO FIELDS, THE ADDRESS OF THE
!		DELETED RECORD AND ITS LENGTH IN WORDS. THIS TABLE
!		IS FILLED IN BY SCANNING ALL RECORDS IN THE BUCKET
!		BEFORE ANY COMPRESSION IS DONE. THEN, THE TABLE
!		IS SCANNED AGAIN AND CONTIGUOUS RECORD CHUNKS ARE
!		COMPRESSED AS A UNIT. IF THE TABLE IS NOT BIG ENOUGH
!		TO HOLD ALL THE DELETED RECORDS IN THE BUCKET, THEN
!		THE ALGORITHM WILL STOP AND PROCESS ONLY THOSE RECORDS
!		IN THE TABLE. THE FORMAT OF THE DELETED-RECORD TABLE IS AS FOLLOWS:
!
!			!-------------------------------------!
!			!    ADDRESS OF    !    LENGTH OF     !
!			!  DELETED RECORD-1!  DELETED RECORD-1!
!			!-------------------------------------!
!			!                 .                   !
!			!                 .                   !
!			!
!
!		NOTE THAT THE "DTADDRESS" FIELD IN THIS TABLE
!		CONTAINS THE ABSOLUTE ADDRESS OF THE DELETED RECORD
!		WHEN THE BUCKET WAS FIRST SCANNED. HOWEVER, AS RECORDS
!		ARE COMPRESSED OUT OF THE BUCKET, THESE ADDRESSES MUST
!		BE MODIFIED BY THE AMOUNT OF DATA COMPRESSED OUT UP
!		TO THAT RECORD. SO, PLEASE NOTE THE USE BELOW OF
!		"AMOUNTCOMPRESED" WHICH CONTAINS THE SIZE OF ALL RECORDS
!		SQUEEZED OUT OF THE BUCKET PREVIOUSLY.
!
!
!	5.	THIS ROUTINE DOES NOT COMPRESS THE LAST DATA RECORD
!		IN THE BUCKET IF IT IS DELETED. THIS TECHNIQUE IS USED
!		BECAUSE IF THE LAST RECORD WERE COMPRESSED, THEN THE
!		HIGH-KEY VALUE IN THE INDEX RECORD WOULD NOT BE CORRECT,
!		AND LATER SEARCHES TO THE BUCKET MIGHT HAVE TO GO
!		TO THE NEXT BUCKET IN ORDER TO FIND THE CORRECT POSITION.
!
!	6.	AS EACH DELETED RECORD IS SCANNED, THE "RRVUPD" BIT
!		IS CLEARED. THIS IS DONE SO THAT "COMPRRV" WILL HAVE
!		SOME MEANS OF KNOWING IF THE RRV FOR A PARTICULAR
!		RECORD HAS ALREADY BEEN UPDATED.
!
!	7.	**** WITH THE ARRIVAL OF EXTENDED ADDRESSING...***
!		THIS ROUTINE WILL HAVE TO BE CHECKED FOR USE OF
!		SECTIONS NUMBERS. FOR EXAMPLE, THE ADDRESS WHICH
!		ARE KEPT IN THE DELETED-RECORD TABLE ARE ABSOLUTE
!		18-BIT ADDRESSES AND HENCE ARE RELATIVE TO THE
!		RMS DATA SECTION.

%([ ***DEFINITION OF SPECIAL DELETED-RECORD TABLE*** ])%

MACRO	DTADDRESS = LH %,	! ADDRESS OF THIS DELETED RECORD
	DTLENGTH  = RH %;	! LENGTH OF THIS DELETED RECORD
%([ ***END OF DEFINITION OF DELETED-RECORD TABLE*** ])%


GLOBAL ROUTINE COMPRESS ( RECDESC, DATABD ) =
BEGIN
	ARGUMENT	(RECDESC,BASEADD);		! RECORD DESC PACKET
	ARGUMENT	(DATABD,BASEADD);		! BUCKET

MAP
    DATABD:	POINTER,
    RECDESC:	POINTER;

REGISTER
    MOVINGPTR:	POINTER,	! TEMPORARY RECORD POINTER
    TEMPAC,			! TEMP AC
    LNGOFTHISRECORD;	! SIZE IN WORDS OF CURRENT RECORD


LOCAL
    CHUNKPTR:	POINTER,	! PTR TO START OF CURRENT CHUNK OF RECORDS
    I,			! USED FOR LOOPING
    CHUNKSIZE,		! SIZE OF THE CHUNK
    DELETECOUNT,		! # OF DELETED RECORDS IN TABLE
    RECLAIMEDSPACE,		! AMOUNT OF SPACE COMPRESSED
    AMOUNTCOMPRESED,	! AMOUNT OF DATA ALREADY COMPRESSED
    LNGOFLASTRECORD,	! SIZE OF LAST RECORD SCANNED
    LNGOFSAVED,		! SIZE OF LAST UNDELETED REC
    BKTPTR:	POINTER,		! PTR TO BUCKET
    ENDPTR:	POINTER,		! PTR TO END OF BUCKET
    AMOUNTTOMOVE,		! AMOUNT TO DATA TO MOVE UP
    INSERTPTR:	POINTER,	! PLACE WHERE NEW RECORD IS TO GO
    LASTPTR,		! PTR TO HIGH RECORD, FOR SPLIT
    OURTABLE,		! FLAG WHICH IS SET IF TABLE IS TOO SMALL
    TABLEINDEX,		! USED TO INDEX DELETE TABLE
    RRVBUCKET;		! BUCKET NUMBER OF RRV

LITERAL	EMPTY = 0,		! VALUES USED FOR "TABLE" FLAG
	FULL = 1;

	%([ DEFINITION OF THE DELETED-RECORD TABLE. NOTE THAT IT CONTAINS
	   1 MORE WORD THAN THE MAX NUMBER OF DELETED RECORD. THIS IS SO
	   THE LAST WORD OF THE TABLE CAN BE ZEROED SO WE WON'T HAVE TO
	   WORRY ABOUT CHECKING IF THE TABLE IS BIG ENOUGH, ETC... ])%

LITERAL	MAXDELETECOUNT = 200;	! MAX OF 200 RECORDS CAN BE COMPRESSED
LOCAL
    DELETETABLE:	FORMATS[ MAXDELETECOUNT + 1 ];

EXTERNAL ROUTINE
    COMPRRV;

LABEL	SCANLOOP,INNERLOOP;


	TRACE ('COMPRESS');

	%IF DBUG %THEN
	IF NOT PRIMARYKEY THEN RMSBUG ( MSGKDB );
	%FI

	%([ IF THIS IS A NEW FILE, OR IF DUPLICATES ARE ALLOWED IN
	   THE PRIMARY KEY, THEN WE CAN EXIT IMMEDIATELY. ])%

	IF ( DUPLICATES ) OR ( NEWFILE ) THEN RETURN ZERO;

	%([ CLEAR SOME VARIABLES ])%

	RECLAIMEDSPACE = ZERO;
	DELETECOUNT = ZERO;
	LNGOFLASTRECORD = ZERO;
	LNGOFSAVED = ZERO;
	OURTABLE = EMPTY;				! ASSUME THE TABLE IS BIG ENOUGH

	%([ FETCH ADDR OF HI REC (FOR SPLIT), AND
	  THE ADDRESS WHERE THE NEW RECORD IS TO GO ])%

	LASTPTR = .RECDESC [ RDLASTRECPTR ];
	INSERTPTR = .RECDESC [ RDRECPTR ];

	%([ ***NEXT STATEMENT MUST BE ADJUSTED TO USE SECTION NUMBER***])%
	BKTPTR = .DATABD [ BKDBKTADR ];		! ADDRESS OF BUCKET
	MOVINGPTR = .BKTPTR + BHHDRSIZE;		! SCANNING PTR

	%([ WE MUST NOW SCAN ALL RECORDS IN THE BUCKET AND SET UP
	   THE TABLE OF DELETED RECORDS. NOTE THAT THIS LOOP IS
	   SOMEWHAT INVERTED...IT CHECKS RECORD-N BEFORE IT ADDS
	   THE LENGTH OF RECORD-(N-1) INTO THE TOTAL COMPRESSED SPACE.
	   THIS IS DONE SO THE LAST RECORD WILL NEVER BE COMPRESSED OUT.
	   NOTICE THAT WE CHECK FIRST TO SEE IF WE ARE AT THE END OF THE
	   BUCKET. IF NOT, THEN WE ADD THE LENGTH OF THE LAST RECORD (IF IT
	   WAS DELETED). THIS ALGORITHM IS STRAIGHTFORWARD EXCEPT THAT
	   IT REQUIRES A FINAL CHECK AFTER THE LOOP TO SEE IF THE NEW
	   RECORD POSITION IS AT THE END OF THE BUCKET.
	   NOTE THAT IF THE DELETE-TABLE FILLS UP (VERY RARE), THEN
	   WE MUST EXIT FROM THE LOOP. BUT FIRST, WE MUST CHECK TO
	   MAKE SURE THAT THE NEW RECORD POSITION HAS BEEN UPDATED
	   PROPERLY.   ])%
SCANLOOP: BEGIN

![125]	UNTIL ( ( RRVFLAG ( MOVINGPTR ) ISON )	! UNTIL WE FIND AN RRV
![125]			OR
![125]		( .MOVINGPTR IS .BKTPTR + .BKTPTR [ BHNEXTBYTE ] ) )
	UNTIL ( IF .MOVINGPTR GEQ .BKTPTR + .BKTPTR [ BHNEXTBYTE ] ![125]
		  THEN true		![125] end of bucket  or
		  ELSE				![125]
		    ( RRVFLAG ( MOVINGPTR ) ISON ) ) ![125] UNTIL WE FIND AN RRV
	DO	%(THIS LOOP)%

		BEGIN

		%([ IF WE ARE NOW AT THE HIGH REC POSITION OR
		   THE RECORD POSITION WHERE THE
		   NEW RECORD IS TO BE INSERTED, WE MUST ADJUST THE RESPECTIVE
		   POINTER TO ACCOUNT FOR THE RECORDS WHICH ARE GOING TO
		   BE SQUEEZED OUT. ])%

		IF .MOVINGPTR IS .LASTPTR
		THEN
			DEC ( RECDESC [ RDLASTRECPTR ], (.RECLAIMEDSPACE + .LNGOFLASTRECORD) );
		IF .MOVINGPTR IS .INSERTPTR
		THEN
			DEC ( RECDESC [ RDRECPTR ], (.RECLAIMEDSPACE + .LNGOFLASTRECORD) );

		LOOKAT ('	CHECKING REC AT: ', MOVINGPTR );

		%([ WE CAN NOW ADD IN THE LENGTH OF THE LAST RECORD IF
		   IT WAS DELETED... ])%

		LOOKAT ('	LENGTH-OF-LAST: ', LNGOFLASTRECORD );
		IF .LNGOFLASTRECORD ISNT ZERO
		THEN	%(LAST RECORD WAS DELETED)%
			BEGIN
			INC ( RECLAIMEDSPACE, .LNGOFLASTRECORD );
			INC ( DELETECOUNT, 1 )
			END;	%(OF IF .LENGTHOFLAST ISNT ZERO)%

		LNGOFLASTRECORD = ZERO;		! CLEAR FOR NEXT ITERATION
		LNGOFTHISRECORD = SIZEOFUDR (  MOVINGPTR );
		LOOKAT ('	LENGTH-OF-RECORD: ', LNGOFTHISRECORD );

		%([ CHECK IF THIS RECORD IS DELETED ])%

		IF CHKFLAG ( MOVINGPTR [ DRFLAGS ], FLGDELETE+FLGNOCOMPRESS)
				IS
			FLGDELETE
		THEN	%(RECORD IS DELETED AND COMPRESSABLE)%

			BEGIN
			RTRACE (%STRING('	RECORD IS DELETED..'));

			%([ IF OUR TABLE IS NOT BIG ENOUGH TO HOLD
			   ANY MORE RECORDS, LET'S FORGET THE WHOLE
			   THING AND NOT SEARCH ANY MORE RECORDS ])%

			IF .DELETECOUNT IS MAXDELETECOUNT
			THEN	%(THE TABLE HAS FILLED UP)%
				BEGIN
				OURTABLE = FULL;		! REMEMBER THIS FACT
				IF .MOVINGPTR LSS .LASTPTR
				THEN DEC (RECDESC [RDLASTRECPTR], .RECLAIMEDSPACE);
							!ADJ HI REC POS
				IF .MOVINGPTR LSS .INSERTPTR
				THEN DEC ( RECDESC [ RDRECPTR ], .RECLAIMEDSPACE );
							!ADJUST THE RECORD POS
				LEAVE SCANLOOP		! EXIT FROM LOOP
				END;	%(OF IF TABLE IS FILLED UP)%

			%([ SAVE THE DELETED RECORD INFO ])%
			%([ ***NOTE NO SECTION NUMBERS ARE SAVED***])%

			DELETETABLE [ .DELETECOUNT, DTADDRESS ] = .MOVINGPTR;
			DELETETABLE [ .DELETECOUNT, DTLENGTH ] = .LNGOFTHISRECORD;

			%([ SAVE THE LENGTH OF THIS RECORD FOR NEXT ITERATION ])%

			LNGOFLASTRECORD = .LNGOFTHISRECORD;

			%([ WE NOW MUST CLEAR THE "RRVUPD" FLAG BIT IN
			   THIS RECORD. THIS IS DONE SO THAT WE CAN LATER
			   DETERMINE IF WE HAVE SQUEEZED OUT THE RRV FOR
			   THIS RECORD. ])%

			CLRFLAG ( MOVINGPTR [ DRFLAGS ], FLGRRVUPD );

			!SEE IF THIS REC NEEDED BY SPLIT FOR IDX UPDATE
			!IF YES, BACK UP HI PTR TO ACCT FOR COMPRESSING THIS REC

			IF .MOVINGPTR IS .LASTPTR
			THEN DEC (RECDESC[RDLASTRECPTR], .LNGOFSAVED);

		END %(OF IF THIS RECORD IS DELETED)%
		ELSE	LNGOFSAVED = .LNGOFTHISRECORD;
						!TO BE ABLE TO PT AT REC BEFORE
						!HI REC WHEN IT COMPRESSED TOO


		%([ BUMP THE TEMPORARY POINTER ])%

		INC ( MOVINGPTR, .LNGOFTHISRECORD )
		END;	%(OF UNTIL WE HAVE SCANNED THE ENTIRE BUCKET)%

	END; %( OF SCANLOOP )%

	LOOKAT ('	TOTAL RECLAIMED SPACE: ', RECLAIMEDSPACE );

	%([ DID WE FIND ANY DELETED RECORDS? ])%

	IF .DELETECOUNT IS ZERO THEN RETURN ZERO;

	%([ WE NOW MUST MAKE 1 FINAL CHECK TO SEE IF THE NEW RECORD
	   IS TO GO AT THE END OF THE BUCKET. IF SO, THEN WE HAVE
	   NOT ADJUSTED THE POINTER BECAUSE WE LEFT THE LOOP ABOVE
	   WHEN WE HIT THE BUCKET END. NOTE ALSO THAT THE TABLE MUST
	   NOT BE FULL. IF THE TABLE FILLED UP, THEN THE CONTENTS
	   OF MOVINGPTR AND INSERTPTR WOULD BE THE SAME, BUT THE
	   CORRECT VALUE HAS ALREADY BEEN SUBTRACTED FROM INSERTPTR.  ])%

	IF ( .MOVINGPTR	%(AT THE END OF THE BUCKET)%
		IS
	   .INSERTPTR )	%(POSITION OF NEW RECORD)%

		AND
	( .OURTABLE ISNT FULL )
	THEN
		DEC ( RECDESC [ RDRECPTR ], .RECLAIMEDSPACE );


	%([ WE WILL NOW CLEAR THE LAST ENTRY IN OUR DELETE-TABLE
	   SO THAT WE CAN USE IT LATER FOR COMPARISONS. WE
	   DON'T HAVE TO CHECK FOR THE END OF THE TABLE BECAUSE
	   OF THE EXTRA WORD WE ALLOCATED ABOVE.  ])%

	DELETETABLE [ .DELETECOUNT, DTADDRESS ] = ZERO;

	%([ WE ARE NOW READY TO SCAN THE LIST OF DELETED RECORDS
	   AND COMPRESS THEM OUT. THE BASIC APPROACH
	   IS TO SCAN THE LIST UNTIL WE FIND A DELETED RECORD
	   WHICH IS NOT CONTIGUOUS WITH THE OTHER DELETED RECORDS.
	   WE THEN CAN SQUEEZE THE ENTIRE CHUNK WHICH WE HAVE COMPUTED
	   UP TO THAT RECORD. WE WILL ALSO SCAN THE LIST TO MAKE SURE
	   THAT WE SQUEEZE ALL RRV'S IN THE SAME BUCKET AT THE SAME
	   TIME (ACTUALLY, THIS IS DONE IN "COMPRRV"). ])%

	TABLEINDEX = ZERO;			! CLEAR OUR MAJOR INDEX
	AMOUNTCOMPRESED = ZERO;		! NO RECORDS SQUEEZED YET

	%([ DO THIS LOOP UNTIL WE SCAN THE ENTIRE TABLE. NOTE
	   THAT TABLEINDEX IS UPDATED BELOW. ])%

	UNTIL .TABLEINDEX GEQ .DELETECOUNT
	DO	%(THIS LOOP)%

		BEGIN
		LOOKAT ('	NEW INDEX: ', TABLEINDEX );

		%([ GET THE ADDRESS AND SIZE OF THE CURRENT DELETED RECORD ])%

		%([ ***SET UP SECTION NUMBERS HERE***])%

		%([ NOTE THAT WE MUST ADJUST THE ADDRESS OF THIS RECORD
		   BY THE AMOUNT OF DATA WHICH HAS BEEN COMPRESSED ALREADY. ])%

		CHUNKPTR = .DELETETABLE [ .TABLEINDEX, DTADDRESS ] - .AMOUNTCOMPRESED;
		MOVINGPTR = .CHUNKPTR;
		CHUNKSIZE = .DELETETABLE [ .TABLEINDEX, DTLENGTH ];

		%([ WE WILL NOW SCAN FROM THIS RECORD TO THE END
		   OF THE TABLE LOOKING FOR A CONTIGUOUS SET
		   OF DELETED RECORDS ])%

		I= .TABLEINDEX;				! SCAN REST OF TABLE
INNERLOOP: BEGIN
          	UNTIL .I IS  .DELETECOUNT - 1
		DO
			BEGIN
			LOOKAT ('	I IS: ', I );

			%([ IS THIS CHUNK CONTIGUOUS WITH THE NEXT DELETED RECORD? ])%

			IF .DELETETABLE [ .I, DTADDRESS ] + .DELETETABLE [ .I, DTLENGTH ]
				IS
			   .DELETETABLE [ .I+1, DTADDRESS ]
			THEN	%(WE CAN INCLUDE NEW RECORD IN CHUNK)%

				BEGIN
				LOOKAT ('	CONTIG CHUNK AT: ', DELETETABLE [ .I+1,DTADDRESS]);
				INC ( CHUNKSIZE, .DELETETABLE[.I+1,DTLENGTH]);
				INC ( I, 1 )
				END	%(OF IF NEXT RECORD IS CONTIGUOUS)%

			ELSE	%(THIS RECORD IS NOT CONTIGUOUS)%
				LEAVE INNERLOOP

			END;	%(OF UNTIL WE SCAN REST OF TABLE)%

		END; %( OF INNERLOOP )%
		%([ DID WE FIND A CHUNK TO SQUEEZE OUT? ])%

		IF .CHUNKSIZE ISNT ZERO
		THEN
			BEGIN	%(TO SQUEEZE THIS CHUNK OUT)%
			LOOKAT ('	CHUNK IS AT: ', CHUNKPTR );
			LOOKAT ('	TOTAL CHUNK-SIZE IS: ', CHUNKSIZE);

			CALLCOMPRRV (	%(COUNT)%	LCI ( DELETECOUNT ),
					%(SIZE)%	LCI ( CHUNKSIZE ),
					%(HOLE)%	LCI ( AMOUNTCOMPRESED ),
					%(INDEX)%	LCI ( TABLEINDEX ),
					%(TABL)%	LCT ( DELETETABLE ),
					%(BKT)%	BPT ( DATABD ) );

			%([ NOW, SQUEEZE OUT THIS CHUNK. IT STARTS AT
			   "CHUNKPTR" AND EXTENDS FOR "CHUNKSIZE" WORDS ])%

			ENDPTR = .BKTPTR + .BKTPTR [ BHNEXTBYTE ];
			AMOUNTTOMOVE = .ENDPTR - .CHUNKPTR - .CHUNKSIZE;
			LOOKAT ('	AMOUNT-TO-MOVE:', AMOUNTTOMOVE );

			IF .AMOUNTTOMOVE ISNT ZERO
			THEN
				MOVEWORDS (	%(FROM)%	.CHUNKPTR+.CHUNKSIZE,
						%(TO)%	.CHUNKPTR,
						%(SIZE)%	.AMOUNTTOMOVE );

			%([ INCREMENT THE AMOUNT OF DATA ALREADY GONE ])%

			INC ( AMOUNTCOMPRESED, .CHUNKSIZE );
			LOOKAT ( '	AMNT-COMPRESSED: ', AMOUNTCOMPRESED);

			%([ ADJUST THE BUCKET HEADER INFORMATION ])%

			DEC ( BKTPTR [ BHNEXTBYTE ], .CHUNKSIZE )
			END;	%(OF IF CHUNKSIZE ISNT ZERO)%

		%([ WE HAVE NOW COMPRESSED A SINGLE CHUNK. SO, BUMP
		   OUR PRIMARY TABLE INDEX POINTER OVER THE ENTIRE CHUNK ])%

		TABLEINDEX = .I + 1
		END;	%(OF UNTIL .TABLEINDEX IS DELETECOUNT)%

!+								       !A54
!   Before leaving, check to make sure the record		       !A54
!   descriptor still has the correct attributes flagged;	       !A54
!   if the record was a duplicate of a deleted record, we	       !A54
!   could have squeezed out the deleted record and the		       !A54
!   duplicate flag should be cleared.				       !A54
!-								       !A54
								       !A54
    IF duplicateflag (recdesc) ison				       !A54
    THEN							       !A54
	! Duplicate flag is on                                         !A54
	BEGIN							       !A54
	LOCAL							       !A54
	    data_pointer;					       !A54
	! Point at the data in the last record, which was a duplicate  !A54
	data_pointer = .recdesc [rdrecptr] + .kdb [kdbhsz];	!A54!M460
!data_pointer = .recdesc [rdlastrecptr] + .kdb [kdbhsz];	       !A54
	IF callckeyku (bpt (recdesc), lpt (data_pointer)) NEQ true     !A54
	THEN							       !A54
	    ! Record is no longer a duplicate, turn off dup flag       !A54
	    clrflag (recdesc [rdstatus], rdflgdup);		       !A54
	END;							       !A54


! We are finally through

	LOOKAT ('	VALUE RETURNED FROM COMPRESS: ', RECLAIMEDSPACE );

	RETURN .RECLAIMEDSPACE

END; ! of COMPRESS



! COMPRRV
! =======

! ROUTINE TO COMPRESS OUT RRV RECORDS WHEN A BUCKET OF PRIMARY
!	DATA RECORDS IS BEING COMPRESSED. THIS ROUTINE IS NOT A
!	GENERAL-PURPOSE ONE IN THAT IT CANNOT BE CALLED BY ANY
!	ROUTINE OTHER THAN "COMPRESS".
!	THIS ROUTINE WILL SEARCH ONLY THE CURRENT CHUNK OF DELETED
!	RECORDS TO SEE IF ANY OF THEM NEED AN RRV SQUEEZED OUT. THE
!	BASIC ALGORITHM FOLLOWED BY THIS ROUTINE IS:
!
!		A )	SEARCH EACH DELETED RECORD IN CHUNK TO SEE
!			IF ANY HAVE RRV'S TO BE COMPRESSED.
!
!		B )	IF THERE IS ONE, SEARCH ENTIRE REST OF TABLE FOR
!			OTHER RECORDS WHICH HAVE RRV'S ON THAT BUCKET. IF
!			FOUND, COMPRESS THE RRV AND MARK THE RECORD AND BEING
!			DONE.
!
!
!	THIS ROUTINE CURRENTLY SQUEEZES OUT THE RRV RECORDS COMPLETELY.
!	IT MAY BE DESIREABLE IN THE FUTURE TO MERELY INDICATE THAT THE
!	RRV'S ARE DELETED, THUS SAVING THE FAIRLY EXPENSIVE MOVING OPERATIONS.
!
!	THIS ROUTINE ATTEMPTS TO OPTIMIZE ACCESS TO THE RRV'S BY
!	SQUEEZING OUT ALL RRV'S IN THE SAME BUCKET AT THE SAME TIME.

! INPUT:
!	DELETECOUNT		# OF DELETED RECORDS IN THE TABLE
!	CHUNKSIZE		SIZE OF CURRENT CHUNK OF DELETED RECORDS
!	AMOUNTCOMPRESED	SIZE OF RECORDS ALREADY SQUEEZED FROM BUCKET
!	TABLEINDEX		INDEX INTO TABLE OF CURRENT DELETED RECORD
!	DELETETABLE		TABLE OF DELETED RECORDS (SEE COMPRESS FOR FORMAT)
!	DATABD			BUCKET DESCRIPTOR OF DATA RECORD BUCKET

! OUTPUT:
!	<NO STATUS RETURNED>

! INPUT ARGS MODIFIED:
!	<NONE>

! ROUTINES CALLED:
!	GETBKT
!	SDATABKT

! NOTES:
!
!	1.	THIS ROUTINE ASSUMES THAT THE RRV'S ARE ALWAYS
!		POSITIONED ON THE BUCKET IN THE SAME RELATIVE
!		ORDER AS THE PRIMARY DATA RECORDS. THIS ASSUMPTION
!		IS VALID BECAUSE RRV'S ARE ALWAYS CREATED BY
!		SCANNING THE DATA RECORDS SEQUENTIALLY FROM TOP
!		TO BOTTOM OF THE BUCKET. THIS ALGORITHM IS IMPLEMENTED
!		IN "UPDRRV".
!
!

GLOBAL ROUTINE COMPRRV ( DELETECOUNT, CHUNKSIZE, AMOUNTCOMPRESED, TABLEINDEX, DELETETABLE, DATABD ) : NOVALUE=
BEGIN
	ARGUMENT	(DELETECOUNT,VALUE);
	ARGUMENT	(CHUNKSIZE,VALUE);
	ARGUMENT	(AMOUNTCOMPRESED,VALUE);
	ARGUMENT	(TABLEINDEX,VALUE);
	ARGUMENT	(DELETETABLE,BASEADD);
	ARGUMENT	(DATABD,BASEADD);

MAP
    DELETETABLE:	POINTER,
    DATABD:	POINTER;

REGISTER
    MOVINGPTR:	POINTER,	! TEMPORARY RECORD POINTER
    TEMPAC;			! TEMPORARY AC
LABEL ITERATION;		! LABEL USED FOR LEAVING

LOCAL
    SIZECOUNTER,		! SIZE OF CHUNK ALREADY SCANNED
    RRVBUCKET,		! BUCKET NUMBER OF BUCKET WITH RRV
    BUCKETSIZE,		! SIZE OF DATA BUCKET
    RRVBD:	FORMATS[ BDSIZE ], ! BKT DESCRIPTOR FOR RRV BUCKET
    MYRECDESC:	FORMATS[ RDSIZE ],	! A TEMP RECORD DESCRIPTOR
    RRVBKTPTR:	POINTER,	! PTR TO BUCKET OF RRV'S
    RRVENDPTR:	POINTER,	! PTR TO END OF RRV BUCKET
    RRVPTR:	POINTER,		! PTR TO ACTUAL RRV
    AMOUNTTOMOVE;		! AMOUNT OF DATA TO MOVE UP

LITERAL NOLOCK = FALSE;		! SYMBOL USED FOR LOCKING
LITERAL UPDATERRVBUCKET = TRUE;	! SYMBOL USED TO DETERMINE IF THE
				! BUCKET OF RRV'S IS WRITTEN IMMEDIATELY
				! TO DISK


	TRACE ('COMPRRV');

	%([ GET SIZE OF EACH DATA BUCKET ])%

	BUCKETSIZE = .KDB [ KDBDBKZ ];		! GET  BUCKET SIZE

	SIZECOUNTER = ZERO;			! CLEAR COUNTER

	%([ WE WILL NOW SCAN THE ENTIRE CHUNK LOOKING FOR ANY
	   RECORD WHICH NEEDS AN RRV ])%

	UNTIL .SIZECOUNTER IS .CHUNKSIZE
	DO
		BEGIN
		IF .SIZECOUNTER GTR .CHUNKSIZE THEN RMSBUG ( MSGCOUNT );

		%([ GET THE ADDRESS OF THIS DELETED RECORD AND ADJUST
		   IT BY THE AMOUNT OF DATA WHICH HAS ALREADY BEEN
		   COMPRESSED OUT OF THE BUCKET ])%

		MOVINGPTR = .DELETETABLE [ .TABLEINDEX, DTADDRESS ] -.AMOUNTCOMPRESED;

		%([ DOES THIS RECORD HAVE AN RRV? ])%

		RRVBUCKET = .MOVINGPTR [ DRRRVBUCKET ];
		IF .RRVBUCKET ISNT .DATABD [ BKDBKTNO ]
		THEN
			%([ AND HAS THE RECORD BEEN PROCESSED? ])%

			IF CHKFLAG ( MOVINGPTR [ DRFLAGS ], FLGRRVUPD ) IS OFF
			THEN
			BEGIN
			LOOKAT ('	SQUEEZE AN RRV AT: ', MOVINGPTR );

			%([ GET A BUCKET TO USE FOR THE RRV BUCKET ])%

			IF 	CALLGETBKT (	%(NUMBER)%	LCI ( RRVBUCKET ),
					%(SIZE)%		LCI ( BUCKETSIZE ),
					%(LOCK)%		PCI ( NOLOCK ),
					%(DESC)%		LCT ( RRVBD ) ) IS FALSE
			THEN
				RETURN RMSBUG ( MSGFAILURE );

			%([ GET POINTER TO TOP OF BUCKET ])%

			RRVBKTPTR = .RRVBD [ BKDBKTADR ];

			%([ WE NOW HAVE A BUCKET TO USE FOR THE RRV'S. LET'S
			   SCAN FROM THIS POINT ON DOWN IN THE TABLE
			   LOOKING FOR ANY RECORD WHICH HAS NOT BEEN CHECKED,
			   BUT WHICH ALSO HAS AN RRV ON THIS BUCKET. ])%

			MYRECDESC [ RDRECPTR ] = ZERO;		! START AT TOP
			MYRECDESC [ WHOLEWORD ] = ZERO;		!****CLEAR FLAGS,STATUS

			INCR J FROM .TABLEINDEX TO .DELETECOUNT -1
			DO
ITERATION:			BEGIN

				%([ GET THE ADDRESS OF THIS RECORD. NOTE THAT
				   THIS CHECK IS REPEATED FOR THE FIRST RECORD
				   IN THE CHUNK. THIS ALLOWS THE LOOP TO BE
				   MADE SIMPLER. ])%

				MOVINGPTR = .DELETETABLE [ .J,DTADDRESS ] -.AMOUNTCOMPRESED;
				%([ HAS IT BEEN CHECKED, AND DOES IT HAVE AN
				   RRV ON THIS BUCKET? ])%

				IF CHKFLAG ( MOVINGPTR [ DRFLAGS],FLGRRVUPD ) IS OFF
				THEN IF ( .MOVINGPTR [ DRRRVBUCKET ]
						IS
					.RRVBUCKET )
				THEN
					BEGIN

					%([ SET THIS RECORD AS BEING DONE ])%

					SETFLAG ( MOVINGPTR [DRFLAGS],FLGRRVUPD);
					LOOKAT ('	REC ALSO NEEDS RRV AT:',MOVINGPTR);
					MYRECDESC [ RDRFA ] = .MOVINGPTR[DRRRVADDRESS];

					%([ LOCATE THE RRV ])%
					IF CALLSDATABKT (LCT ( MYRECDESC ),
							LCT ( RRVBD ) ) IS FALSE
					THEN
						BEGIN
						RTRACE (%STRING('***NOT FIND RRV..'));
						USRSTS = SU$RRV;
						LEAVE ITERATION
						END; %(OF NOT FOUND RRV)%

					%([ GET ADDRESS OF RRV FOUND ])%

					RRVPTR = .MYRECDESC [ RDRECPTR ];
					RRVENDPTR = .RRVBKTPTR+ .RRVBKTPTR[BHNEXTBYTE];
					AMOUNTTOMOVE = .RRVENDPTR-.RRVPTR - RRVRECSIZE;

					%([ SQUEEZE OUT THE RRV ])%

					IF .AMOUNTTOMOVE ISNT ZERO
					THEN
						MOVEWORDS (	%(FROM)%	.RRVPTR+RRVRECSIZE,
								%(TO)%		.RRVPTR,
								%(SIZE)%	.AMOUNTTOMOVE);

					%([ ADJUST THE BUCKET HEADER ])%

					DEC ( RRVBKTPTR [ BHNEXTBYTE], RRVRECSIZE)

					END	%(OF IF THIS RECORD HAS AN RRV)%
				END; %(OF INCR J LOOP)%

			%([ WE HAVE FINISHED COMPRESSING ALL RRV'S
			   ON THIS BUCKET..RETURN THE BUCKET ])%

			CALLPUTBKT (	%(UPDATE)%	PCI (UPDATERRVBUCKET),
					%(DESC)%		LCT ( RRVBD ) )
			END; %(OF OF THIS RECORD HAS AN RRV)%

		%([ NOW, BUMP THE SIZE OF THIS CHUNK ])%

		INC ( SIZECOUNTER, .DELETETABLE[.TABLEINDEX, DTLENGTH ] );
		LOOKAT ('	SIZE-COUNTER:', SIZECOUNTER);
		INC ( TABLEINDEX, 1 )			! BUMP OUR COUNTER

		END;	%(OF UNTIL SIZECOUNTER IS CHUNKSIZE)%

	RETURN

END;	%(OF COMPRRV)%


! UPDRRVS
! =======
!
!	     THIS ROUTINE PERFORMS UPDATING OF RRV RECORDS WHEN A DATA BUCKET
!	SPLITS DUE TO A RECORD INSERTION.  THIS INCLUDES ASSIGNING NEW
!	ID'S TO ALL RECORDS WHICH WERE MOVED.  IF A RECORD WAS MOVED FOR
!	THE FIRST TIME, THEN AN RRV RECORD IS CREATED IN THE ORIGINAL
!	BUCKET. IF A RECORD WAS MOVED PREVIOUSLY, THEN THE RRV RECORD
!	IN THE ORIGINAL BUCKET IS UPDATED.
!
! INPUT:
!	OLDBKD = BASE ADDRESS OF OLD BUCKET DESCRIPTOR
!	NEWBKD = BASE ADDRESS OF NEW BUCKET DESCRIPTOR
!
! OUTPUT:
!	TRUE = SUCCESSFUL OPERATION
!	FALSE = RRV RECORD NOT FOUND
!
! ROUTINES CALLED:
!	ALCRFA
!	FBYRFA
!	PUTBKT
!
! NOTES:1)     THIS ROUTINE ATTEMPTS TO OPTIMIZE THE UPDATING PROCESS BY
!	   UPDATING ALL RRV RECORDS THAT ARE IN THE SAME BUCKET AT THE
!	   SAME TIME.  'FLGRRVUPD' WILL BE SET FOR EACH RECORD WHOSE RRV
!	   RECORDS HAVE BEEN UPDATED.  IT IS POSSIBLE THAT THIS FLAG BIT
!	   MAY BE WRITTEN OUT TO THE FILE. THIS WOULD BE CAUSED BY
!	   A SYSTEM CRASH IN THE MIDDLE OF THE UPDATE PROCESS.
!	   THEREFORE, THESE FLAG BITS MUST BE CLEARED BEFORE ANY RRV
!	   RECORD UPDATING IS ATTEMPTED.

!
!	2) IT IS ALSO ASSUMED THAT IF A SET OF RECORDS IN THE NEW BUCKET
!	   HAVE RRV RECORDS IN A COMMON BUCKET THAT THE RRV RECORDS
!	   WILL BE IN THE SAME ORDER AS THE DATA RECORDS.  THIS SPEEDS
!	   UP THE SEARCH FOR RRV RECORDS BECAUSE THE SEARCH FOR THE NEXT
!	   RRV RECORD TO UPDATE CAN CONTINUE FROM WHERE THE LAST RRV
!	   RECORD WAS FOUND. THIS ASSUMPTION IS SATISFIED BECAUSE THIS ROUTINE
!	   ALWAYS CREATES RRV'S BY SCANNING THE DATA RECORDS FROM
!	   THE TOP OF THE BUCKET DOWNWARDS. NOTE THAT IF THIS
!	   ALGORITHM EVER CHANGES (FOR SOME UNKNOWN REASON), SOME
!	   CHANGES MUST BE MADE ALSO TO "COMPRRV".

!
!	3) THIS ROUTINE CAN ALSO PROCESS SIDR RECORDS. HOWEVER,
!	   THE ONLY FUNCTION WHICH MUST BE PERFORMED FOR SIDR'S
!	   IS THAT NEW ID'S MUST BE ALLOCATED FOR EACH SIDR WHICH
!	   MOVED IN THE SPLIT. NOTE ALSO THAT THE NEW BUCKET IS
!	   NOT WRITTEN TO DISK IF IT IS A SECONDARY BUCKET...THIS
!	   IS DONE BY "INSRTSIDR".

GLOBAL ROUTINE UPDRRVS ( OLDBKD, NEWBKD ) =
BEGIN

	ARGUMENT (OLDBKD,BASEADD);	! BASE ADR OF OLD BUCKET DESCRIPTOR
	ARGUMENT (NEWBKD,BASEADD);	! BASE ADR OF NEW BUCKET DESCRIPTOR

MAP
    OLDBKD:	POINTER,
    NEWBKD:	POINTER;

LOCAL
    OLDBKTNO,			! NO. OF OLD BUCKET
    NEWBKTNO,			! NO. OF NEW BUCKET
    RRVBKTNO,			! NO. OF BUCKET WITH RRV RECORD TO BE UPDATED
    BKTSIZE,			! BUCKET SIZE FOR CALL TO 'GETBKT'
    KEYOFREFERENCE,			! USD TO TELL IF SIDR BUCKET
    ERRFLAG,			! FALSE IF AN RRV RECORD WAS NOT FOUND
    SAVRECPTR,			! PTR TO LAST RRV RECORD WE FOUND

    BH:	POINTER,			! PTR TO A BUCKET
    ENDPTR:	POINTER,			! PTR TO END OF A BUCKET
    RECPTR:	POINTER,			! PTR TO CURRENT RECORD IN NEWBKT
    UPDRECPTR:	POINTER,		! PTR TO CURRENT RECORD WHEN RRV'S ARE BEING UPDATED
    RRVRECPTR:	POINTER,		! POINTER TO RRV RECORD
    NEWRRVPTR:	POINTER,		! POINTER TO NEWLY CREATED RRV RECORD
    UPDRRVRFA:	FORMAT,		! RFA FOR RRV RECORD TO BE UPDATED
    RRVRFA:	FORMAT,			! RFA FOR RRV RECORD
    RECDESC:	FORMATS[ RDSIZE ],	! A RECORD DESCRIPTOR ( FOR USE BY 'FBYRFA' )
    UPDBKD:	FORMATS[ BDSIZE ];	! BUCKET DESCRIPTOR FOR UPDATING RRV RECORDS
!REGISTER
!    TMPRFA:	FORMAT;		! TEMP FOR AN RFA

EXTERNAL ROUTINE
    ALCNEWIDS;		! ALLOCATE NEW ID'S FOR RECORDS


	TRACE ( 'UPDRRV' );
	CHECKEXACTCOUNT;			! MUST HAVE EXACT NO. ARGS

	%([ INITIALIZE SOME POINTERS ])%

	KEYOFREFERENCE = .KDB [ KDBREF ];	! GET KEY OF REFER.
	BH = .NEWBKD [ BKDBKTADR ];		! POINTER TO NEW BUCKET
	RECPTR = .BH + BHHDRSIZE;		! POINTER TO CURRENT RECORD IN NEWBKT
	ENDPTR = .BH + .BH [BHNEXTBYTE ];	! POINTER TO END OF NEWBKT

	OLDBKTNO = .OLDBKD [ BKDBKTNO ];	! BUCKET NO. OF OLD BUCKET
	NEWBKTNO = .NEWBKD [ BKDBKTNO ];	! BUCKET NO. OF NEW BUCKET


	%([ ALLOCATE NEW ID'S FOR ALL RECORDS IN THE NEW BUCKET
	   WHICH NEED ONE (I.E., THE NEW RECORD MIGHT NOT NEED ONE) ])%

	CALLALCNEWIDS ( BPT ( NEWBKD ) );

	%([ UPDATE THE NEW BUCKET TO PUT NEWLY ASSIGNED ID'S IN THE FILE ])%

	UPDATEBUCKET ( NEWBKD );			! UPDATE THE NEW BUCKET

	BKTSIZE = .KDB [ KDBDBKZ ];			! GET BUCKET SIZE

	%([ MAIN LOOP -- PROCESS ALL RECORDS IN NEW BUCKET ])%

	ERRFLAG = FALSE;				! START WITH NO "RRV RECORDS NOT FOUND"

	RECPTR = .BH + BHHDRSIZE;			! INIT POINTER TO FIRST RECORD

	UNTIL .RECPTR GEQ .ENDPTR
	DO
		BEGIN %( PROCESS THIS RECORD )%
		RRVRFA = .RECPTR [ DRRRVADDRESS ];
		RRVBKTNO = .RRVRFA [ RFABUCKET ];

		LOOKAT ( 'RECORD ID: ' , RRVRFA [ RFAID ] );


		%([ DECIDE WHETHER TO UPDATE/CREATE RRV RECORD ])%

		IF .RRVBKTNO IS .OLDBKTNO		! FIRST TIME RECORD MOVED?
		THEN
			BEGIN %( CREATE RRV RECORD FOR RECORD WHICH WAS MOVED FOR THE FIRST TIME )%
			BH = .OLDBKD [ BKDBKTADR ];	! POINT TO OLD BUCKET
			NEWRRVPTR = .BH + .BH [ BHNEXTBYTE ];	! POINT TO WHERE NEW RRV RECORD WILL GO
			IF (.NEWRRVPTR - .BH ) GTR (.KDB [ KDBDBKZ ] ^ B2W)	! ROOM IN BKT FOR NEW RRV?
			THEN
				RMSBUG ( MSGCCR );				! NO, ** CAN'T CREATE RRV RECORD **
			INC ( BH [ BHNEXTBYTE ] , RRVRECSIZE );	! ALLOCATE SPACE FOR NEW RRV RECORD

			NEWRRVPTR [ DRFLAGS ] = DEFRRVFLAGS;	! INITIALIZE RRV RECORD FLAGS
			NEWRRVPTR [ DRRECORDID ] = .RRVRFA [ RFAID ];		! INITIALIZE RRV RECORD ID
			NEWRRVPTR [ DRRRVADDRESS ] =
				MAKERFA ( .NEWBKTNO , .RECPTR [ DRRECORDID ] );	! INITIALIZE RRV RECORD POINTER (POINTS TO DATA RECORD)

			LOOKAT ( 'CREATED RRV AT: ' , NEWRRVPTR );

			END	%( OF CREATE RRV )%


		ELSE IF .RRVBKTNO ISNT .NEWBKTNO				! EXISTING RRV RECORD?
				AND
			CHKFLAG ( RECPTR [ DRFLAGS ] , FLGRRVUPD ) IS OFF	! AND ITS RRV RECORDS HAVEN'T BEEN UPDATED?
		THEN
			BEGIN %( UPDATE EXISTING RRV RECORDS ])%
			UPDRECPTR = .RECPTR;			! INIT PTR TO RECORD TO UPDATE RRV
			RECDESC [ RDRECPTR ] = ZERO;		! START SEARCH FROM TOP OF BUCKET
			IF CALLGETBKT (%( BKTNO )%	LCI ( RRVBKTNO ),
					%( BKTSIZE )%	LCI ( BKTSIZE ),
					%( LOCKFLAG )%	PCI ( FALSE ),
					%( BKTDESC )%	LCT ( UPDBKD ) ) IS FALSE
			THEN
				ERRFLAG = TRUE			! REMEMBER THAT BUCKET WAS UNAVAILABLE
			ELSE
				BEGIN	%( LOOP FOR REST OF DATA RECORDS UPDATING
					 ALL RRV RECORDS THAT ARE IN THE SAME BUCKET )%

				DO
					BEGIN %( POSSIBLY UPDATE THIS RRV RECORD )%
					UPDRRVRFA = .UPDRECPTR [ DRRRVADDRESS ];	! GET RFA OF RRV RECORD
					IF .UPDRRVRFA [ RFABUCKET ] IS .RRVBKTNO	! I.E., SAME BUCKET
					THEN
						BEGIN	%( UPDATE THIS RRV RECORD )%
						RECDESC [ RDRFA ] = .UPDRRVRFA;	! SET RFA OF RRV RECORD
						SAVRECPTR = .RECDESC [ RDRECPTR ];	! REMEMBER WHERE WE ARE (IN CASE OF SEARCH FAILURE)
						IF CALLSDATABKT (%( RECDESC )%	LCT ( RECDESC ),
								%( BKTDESC )%	LCT ( UPDBKD ) ) IS FALSE
						THEN
							BEGIN	%( CAN'T GET RRV )%
							ERRFLAG = TRUE;
							RECDESC [ RDRECPTR ] = .SAVRECPTR	! RESTORE SEARCH POINTER
							END	%( OF CAN'T GET RRV RFA )%
						ELSE
							BEGIN	%( UPDATE RRV RECORD )%
							RRVRECPTR = .RECDESC [ RDRECPTR ];	! POINT TO RRV RECORD
							RRVRECPTR [ DRRRVADDRESS ] =
								MAKERFA ( .NEWBKTNO , .UPDRECPTR [ DRRECORDID ] );	! UPDATE RRV TO POINT TO NEW DATA RECORD POSITION
							SETFLAG ( UPDRECPTR [ DRFLAGS ] , FLGRRVUPD );	! FLAG THAT RRV RECORD HAS BEEN UPDATED

							LOOKAT ( 'UPDATED RRV AT RFA: ' , UPDRRVRFA );
							END	%( OF UPDATE RRV RECORD )%
						END;	%( OF UPDATE THIS RRV RECORD )%

					UPDRECPTR = .UPDRECPTR + SIZEOFUDR (  UPDRECPTR );	! NEXT RECORD IN NEW BUCKET
					LOOKAT ('	UPD-RECPTR: ',UPDRECPTR )
					END	%( OF POSSIBLY UPDATE THIS RRV RECORD )%

				UNTIL .UPDRECPTR GEQ .ENDPTR;		! LOOP BACK FOR NEXT NEW BUCKET RECORD


				%([ RELEASE BUCKET USED FOR UPDATING RRV RECORDS ])%

				CALLPUTBKT (	%( UPDATE FLAG )%		PCI ( TRUE ),
						%( BKTDESC )%		LCT ( UPDBKD ) )
				END	%( OF LOOP FOR REST OF RECORDS IN BUCKET... )%
			END;	%( OF UPDATE EXISTING RRV RECORDS )%

		RECPTR = .RECPTR + SIZEOFUDR (  RECPTR )		! NEXT RECORD IN NEW BUCKET

		END;	%( OF PROCESS THIS RECORD FROM NEW BUCKET )%


	%([ DONE ])%

	IF .ERRFLAG
	THEN
		BADRETURN				! ** RRV NOT FOUND **
	ELSE
		GOODRETURN				! SUCCESS

END;	%( OF UPDRRVS )%



! ALCNEWIDS
! =========

! ROUTINE TO ALLOCATE NEW ID'S FOR ALL RECORDS IN A NEW BUCKET
!	AFTER A DATA BUCKET SPLIT. THIS ROUTINE IS CALLED FOR
!	BOTH A PRIMARY AND A SECONDARY DATA BUCKET SPLIT. FOR
!	PRIMARY DATA RECORDS, THIS ROUTINE MUST CHECK TO SEE IF
!	A NEW ID IS NECESSARY (IT MIGHT NOT BE FOR THE NEW RECORD).
!	FOR SECONDARY DATA RECORDS, A NEW ID IS ALWAYS REQUIRED.
!
!	THIS ROUTINE ATTEMPTS TO OPTIMIZE ITS OPERATION BY NOT CALLING
!	"ALCRFA" UNLESS ABSOLUTELY NECESSARY. THAT IS, IT WILL MAINTAIN
!	ITS OWN LOCAL COPIES OF THE "LASTID" AND "NEXTID" FIELDS IN
!	THE BUCKET HEADER AND CALL ALCRFA ONLY WHEN NEXTID IS GREATER
!	THAN LASTID.

! INPUT:
!	NEWBD		BUCKET DESCRIPTOR OF THE NEW BUCKET

! OUTPUT:
!	<NO STATUS RETURNED>

! ROUTINES CALLED:
!	ALCRFA

GLOBAL ROUTINE ALCNEWIDS ( NEWBD ):NOVALUE =
BEGIN
	ARGUMENT	(NEWBD,BASEADD);

MAP
    NEWBD:	POINTER;

LOCAL
    ENDPTR:	POINTER,		! PTR TO END OF BUCKET
    BKTPTR:	POINTER,		! PTR TO TOP OF BUCKET
    LASTID,			! LOCAL COPY OF THE LASTID FIELD
    KEYOFREFERENCE,		! KEY OF REF
    NEWBUCKET;		! BUCKET NUMBER OF NEW BUCKET

REGISTER
    RECORDPTR:	POINTER,	! POINTER TO CURRENT RECORD IN BUCKET
!   CURRENTRFA:	FORMAT,		! RFA OF CURRENT RECORD
    CURRENTRFA,			! RFA OF CURRENT RECORD.PREVIOUS DECL.INVALID IN B36
    NEXTID;			! NEXT ID TO BE ALLOCATED
EXTERNAL ROUTINE
    ALCRFA;


	TRACE ('ALCNEWIDS');

	%([ SET UP SOME BUCKET POINTERS, AND SOME MISC. STUFF ])%

	BKTPTR = .NEWBD [ BKDBKTADR ];		! BKT POINTER
	ENDPTR = .BKTPTR + .BKTPTR [ BHNEXTBYTE ];
	NEWBUCKET = .NEWBD [ BKDBKTNO ];	! NUMBER OF BUCKET
	RECORDPTR = .BKTPTR + BHHDRSIZE;	! INIT RECORD POINTER

	%([ FETCH THE CURRENT CONTENTS OF THE ID FIELDS ])%

	NEXTID = .BKTPTR [ BHNEXTID ];
	LASTID = .BKTPTR [ BHLASTID ];

	%([ IS THIS A PRIMARY OR SECONDARY BUCKET? ])%

	KEYOFREFERENCE = .KDB [ KDBREF ];

	%([ LOOP OVER ALL RECORDS IN THE BUCKET. FOR PRIMARY
	   RECORDS, CLEAR THE "UPDRRV" BIT SO UPDRRVS WILL KNOW
	   IF IT HAS UPDATED THE RRV FOR THAT RECORD. ])%

	UNTIL .RECORDPTR GEQ .ENDPTR
	DO
		BEGIN

		%([ CLEAR THIS SO WE WILL ALWAYS ALLOCATE ID FOR SEC BUCKETS ])%

		CURRENTRFA = ZERO;
		IF .KEYOFREFERENCE IS REFPRIMARY
		THEN
			BEGIN
			CLRFLAG ( RECORDPTR [ DRFLAGS ] , FLGRRVUPD );	! CLEAR "RRVUPD" FLAG
			CURRENTRFA = .RECORDPTR [ DRRRVADDRESS ]	! GET RRV
			END; %( OF IF THIS IS A PRIMARY DATA RECORD)%
		IF .CURRENTRFA < RH > ISNT .NEWBUCKET	! 'RH' replaces 'RFABUCKET'
		THEN	%(WE MUST ALLOCATE A NEW ID)%
			BEGIN

			%([ WE KNOW THAT WE CAN ALWAYS USE THE LOCAL COPIES
			   OF THE LASTID AND NEXTID, SINCE THE BUCKET IS
			   NEW. THUS, IT WAS JUST INITIALIZED AND THE
			   NEXTID STARTED OUT AT 1. ])%

			IF .NEXTID GTR .LASTID
			THEN	%(THERE IS SOMETHING WRONG)%
				RMSBUG ( MSGCOUNT );		! BAD COUNTER
			%([ STORE THIS ID IN THE RECORD ])%

			RECORDPTR [ DRRECORDID ] = .NEXTID;
			INC ( NEXTID, 1 )		! BUMP IT

			END;	%(OF CURRENTRFA ISNT NEWBUCKET)%

		%([ BUMP THE POINTER TO NEXT DATA RECORD ])%

		RECORDPTR = .RECORDPTR + SIZEOFDATARECRD (  RECORDPTR )		! ADVANCE TO NEXT RECORD
		END;	%(OF UNTIL RECORDPTR GEQ ENDPTR)%

	%([ REPLACE THE ID FIELD IN THE BUCKET HEADER ])%

	BKTPTR [ BHNEXTID ] = .NEXTID;

	RETURN

END;	%(OF ALCNEWIDS)%
END
ELUDOM