//		Q U E U E   M A I N T E N A N C E
// Copyright Xerox Corporation 1979

//	E. McCreight
//	last edited March 22, 1976  2:30 PM

external [
	Allocate
	Free
	MoveBlock
	ReadBlock
	Gets
	Puts
	Endofs

	CZ

	GETQF
	GETQR
	GETNWQF
	PUTQF
	PUTQR
	PUTNWQR
	ISEMPTYQ
	APPENDQ
	INITQ
	EMPTYOUTQ
	CHARSINQ
	STREAMTOQR
	QFTOSTREAM
	COPYQ
	COMPAREQ
	QFTOSTRING
	STRINGTOQR
	XFERQWHILE
	GETWFILE
	]

manifest [
	DEFLEN = 20
	]

static [
	OOPS
	SFILE
	]


structure STRING:
	[ length byte
	char↑1,255 byte
	]

structure QS:	[ FRONT word
		REAR word
		FILLERUP word
		]

structure QE:	[ FLINK word
		RLINK word
		MAXLEN word
		FRONT word
		REAR word
		DATA word =
		[ CHAR↑0,0 byte
		] =

		[ WD↑0,0 word
		]

		]




//	When these subroutines are called, and when they return, the
//	two-way queues on which they work have the following format:

//	1) If Q is empty, then Q>>QS.FRONT = Q>>QS.REAR = 0

//	2) If Q is not empty, then it is a doubly-linked list of
//	non-empty queue elements, from Q>>QS.FRONT linked
//	by the QE>.FLINK field, and from Q>>QS.REAR
//	linked by the QE.RLINK field. These lists terminate with
//	a 0 link.

//	3) A queue element Z contains from 1 to Z>>QE.MAXLEN+1
//	characters, addressed by Z>>QE.CHAR↑(Z>>QE>FRONT)
//	through Z>>QE.CHAR↑(Z>>QE.REAR), wrapping around from
//	↑(Z>>QE.MAXLEN) to ↑0 if appropriate.

//	4) Q>>QS.FILLERUP contains the address of a routine
//	which will either add at least one element to Q
//	or return FALSE if it can't.


let ISEMPTYQ(Q) = (Q>>QS.FRONT eq 0)? not ((Q>>QS.FILLERUP)(Q)),
			false



and INITQ(Q, FILLUPRTN; numargs NA) be

	[ if NA ls 2 then FILLUPRTN = NOPE
	Q>>QS.FRONT = 0
	Q>>QS.REAR = 0
	Q>>QS.FILLERUP = FILLUPRTN
	return
	]


and EMPTYOUTQ(Q) be

	[ until ISEMPTYQ(Q) do

		[ let QF = Q>>QS.FRONT
		let NQF = QF>>QE.FLINK
		Free(CZ, QF, lv OOPS)

		test NQF eq 0

		ifso Q>>QS.REAR = 0

		ifnot NQF>>QE.RLINK = 0

		Q>>QS.FRONT = NQF
		]

	return
	]


and NOPE(Q) = false


and GETQF(Q) = valof

	[ while Q>>QS.FRONT eq 0 do
		if (Q>>QS.FILLERUP)(Q) eq false then
			resultis 0

	let QF = Q>>QS.FRONT
	let QEF = QF>>QE.FRONT
	let C = QF>>QE.CHAR↑QEF

	test QEF eq QF>>QE.REAR

	ifso	[ let NF = QF>>QE.FLINK
		test NF eq 0

		ifso Q>>QS.REAR = 0

		ifnot NF>>QE.RLINK = 0

		Free(CZ, QF, lv OOPS)
		Q>>QS.FRONT = NF
		]

	ifnot	[ QEF = QEF+1
		if QEF gr QF>>QE.MAXLEN then QEF = 0
		QF>>QE.FRONT = QEF
		]

	resultis C
	]


and GETQR(Q) = valof

	[ while Q>>QS.REAR eq 0 do
		if (Q>>QS.FILLERUP)(Q) eq false then
			resultis 0

	let QR = Q>>QS.REAR
	let QER = QR>>QE.REAR
	let C = QR>>QE.CHAR↑QER

	test QER eq QR>>QE.FRONT

	ifso	[ let NR = QR>>QE.RLINK
		test NR eq 0

		ifso Q>>QS.FRONT = 0

		ifnot NR>>QE.FLINK = 0

		Free(CZ, QR, lv OOPS)
		Q>>QS.REAR = NR
		]

	ifnot	[ QER = QER-1
		if QER ls 0 then QER = QR>>QE.MAXLEN
		QR>>QE.REAR = QER
		]

	resultis C
	]


and GETNWQF(N, Q, BLK, MoveBlockF; numargs na) = valof

	[ OOPS = false
	let NWTOGO = N
	if na ls 4 then MoveBlockF = MoveBlock

	while (NWTOGO gr 0) & not ISEMPTYQ(Q) & not OOPS do

		[ let QF = Q>>QS.FRONT
		let FX = (QF>>QE.FRONT) rshift 1	// 2 BYTES/WD
		let RX = (QF>>QE.REAR) rshift 1
		let MX = (QF>>QE.MAXLEN) rshift 1

		let NWAVAIL = ((RX ls FX)? MX, RX)+1-FX

		let NWTOXF = (NWAVAIL ls NWTOGO)? NWAVAIL,
				NWTOGO

		let WBLT = MoveBlockF(BLK, lv (QF>>QE.WD↑FX), NWTOXF)
		if MoveBlockF ne MoveBlock & WBLT ne NWTOXF then
			[ NWTOXF = WBLT
			OOPS = true
			]

		test RX eq FX+NWTOXF-1

		ifso	[ let NF = QF>>QE.FLINK
			test NF eq 0

			ifso Q>>QS.REAR = 0

			ifnot NF>>QE.RLINK = 0

			Free(CZ, QF)

			Q>>QS.FRONT = NF
			]

		ifnot	[ FX = FX+NWTOXF
			if FX gr MX then FX = 0

			QF>>QE.FRONT = FX lshift 1
			]

		NWTOGO = NWTOGO-NWTOXF
		BLK = BLK+NWTOXF
		]

	resultis N-NWTOGO
	]


and PUTQF(Q, C) = valof

	[ let QF = Q>>QS.FRONT
	let QEF = nil

	if (QF eq 0) % valof
		[ QEF = QF>>QE.FRONT
		QEF = QEF-1
		if QEF ls 0 then QEF = QF>>QE.MAXLEN
		resultis (QEF eq QF>>QE.REAR)
		] then

		[ let NF = GETNQE(DEFLEN)

		if NF eq false then resultis false

		NF>>QE.FLINK = QF
		NF>>QE.RLINK = 0

		test QF eq 0

		ifso Q>>QS.REAR = NF

		ifnot QF>>QE.RLINK = NF

		NF>>QE.REAR = 0
		QEF = 0
		Q>>QS.FRONT = NF
		QF = NF
		]

	QF>>QE.CHAR↑QEF = C
	QF>>QE.FRONT = QEF
	resultis true
	]


and PUTQR(Q, C) = valof

	[ let QR = Q>>QS.REAR
	let QER = nil

	if (QR eq 0) % valof
		[ QER = QR>>QE.REAR
		QER = QER+1
		if QER gr QR>>QE.MAXLEN then QER = 0
		resultis (QER eq QR>>QE.FRONT)
		] then

		[ let NR = GETNQE(DEFLEN)

		if NR eq false then resultis false

		NR>>QE.RLINK = QR
		NR>>QE.FLINK = 0

		test QR eq 0

		ifso Q>>QS.FRONT = NR

		ifnot QR>>QE.FLINK = NR

		NR>>QE.FRONT = 0
		QER = 0
		Q>>QS.REAR = NR
		QR = NR
		]

	QR>>QE.CHAR↑QER = C
	QR>>QE.REAR = QER
	resultis true
	]


and PUTNWQR(N, Q, BLK, MoveBlockF; numargs na) = valof

	[ let NWTOGO = N
	OOPS = false
	if na ls 4 then MoveBlockF = MoveBlock

	while (NWTOGO gr 0) & not OOPS do

		[ let QR = Q>>QS.REAR
		let FX = nil
		let RX = nil
		let MX = nil

		if (QR eq 0) % valof

			[ FX = (QR>>QE.FRONT) rshift 1
			RX = (QR>>QE.REAR) rshift 1
			MX = (QR>>QE.MAXLEN) rshift 1

			RX = (RX+1 gr MX)? 0, RX+1
			resultis RX eq FX
			] then

			[ let NQE = GETNQE(((DEFLEN ls NWTOGO)?
					NWTOGO, DEFLEN))

			if NQE eq false then
				[ OOPS = true
				loop
				]

			test QR eq 0

			ifso Q>>QS.FRONT = NQE

			ifnot QR>>QE.FLINK = NQE

			NQE>>QE.FLINK = 0
			NQE>>QE.RLINK = QR
			NQE>>QE.FRONT = 0
			QR = NQE
			Q>>QS.REAR = QR

			FX = 0
			RX = 0
			MX = (QR>>QE.MAXLEN) rshift 1
			]

		let NWAVAIL = ((FX gr RX)? FX, MX+1)-RX
		let NWTOXF = (NWAVAIL ls NWTOGO)? NWAVAIL,
				NWTOGO

		let WBLT = MoveBlockF(lv (QR>>QE.WD↑RX), BLK, NWTOXF)

		if MoveBlockF ne MoveBlock & WBLT ne NWTOXF then
			[ NWTOXF = WBLT
			OOPS = true
			]

		QR>>QE.REAR = ((RX+NWTOXF-1) lshift 1)+1
		BLK = BLK+NWTOXF
		NWTOGO = NWTOGO-NWTOXF
		]

	resultis N-NWTOGO
	]


and APPENDQ(RESULT, Q1, Q2) be

	[ test Q1>>QS.FRONT eq 0

		ifso [ RESULT>>QS.FRONT = Q2>>QS.FRONT
			RESULT>>QS.REAR = Q2>>QS.REAR
			]

		ifnot test Q2>>QS.FRONT eq 0

			ifso	[ RESULT>>QS.FRONT = Q1>>QS.FRONT
				RESULT>>QS.REAR = Q1>>QS.REAR
				]

			ifnot	[ let Q1R = Q1>>QS.REAR
				let Q2F = Q2>>QS.FRONT
				Q1R>>QE.FLINK = Q2F
				Q2F>>QE.RLINK = Q1R
				RESULT>>QS.FRONT = Q1>>QS.FRONT
				RESULT>>QS.REAR = Q2>>QS.REAR
				]

	return
	]



and CHARSINQ(Q) = valof

	[ let COUNT = 0
	let QP = Q>>QS.FRONT

	while QP ne 0 do
		[ test QP>>QE.FRONT gr QP>>QE.REAR

		ifso COUNT = COUNT+(QP>>QE.MAXLEN-QP>>QE.FRONT)+
			QP>>QE.REAR+2

		ifnot COUNT = COUNT+(QP>>QE.REAR-QP>>QE.FRONT)+1

		QP = QP>>QE.FLINK
		]

	resultis COUNT
	]


and COPYQ(SQ, DQ) be

	[ let MYQ = vec size QS/16
	INITQ(MYQ)

	until ISEMPTYQ(SQ) do
		[ let C = GETQF(SQ)
		PUTQR(DQ, C)
		PUTQR(MYQ, C)
		]

	APPENDQ(SQ, MYQ, SQ)
	return
	]



and COMPAREQ(Q1, Q2) = valof

	[ let FQ1 = vec size QS/16
	let FQ2 = vec size QS/16

	INITQ(FQ1)
	INITQ(FQ2)

	let Compare = 0

	until ISEMPTYQ(Q1) % ISEMPTYQ(Q2) % (Compare ne 0) do

		[ let C1 = GETQF(Q1)
		PUTQR(FQ1, C1)

		let C2 = GETQF(Q2)
		PUTQR(FQ2, C2)

		if C1 ls C2 then Compare = -1

		if C1 gr C2 then Compare = 1
		]

	if Compare eq 0 &
		((not ISEMPTYQ(Q1)) % (not ISEMPTYQ(Q2))) then

		Compare = ISEMPTYQ(Q2)? 1, -1

	APPENDQ(Q1, FQ1, Q1)
	APPENDQ(Q2, FQ2, Q2)

	resultis Compare
	]


and QFTOSTRING(Q, S) be

	[ let SL = 0
	until ISEMPTYQ(Q) do
		[ SL = SL+1
		S>>STRING.char↑SL = GETQF(Q)
		]
	S>>STRING.length = SL
	return
	]


and STRINGTOQR(S, Q) be

	[ let SL = S>>STRING.length
	for I=1 to SL do PUTQR(Q, S>>STRING.char↑I)
	return
	]


and XFERQWHILE(SRCRTN, PBSRTN, SRCQ, DESTRTN, DESTQ, WHILEFN) be

	[ until ISEMPTYQ(SRCQ) do

		[ let C = SRCRTN(SRCQ)
		test WHILEFN(C)

		ifso DESTRTN(DESTQ, C)

		ifnot	[ PBSRTN(SRCQ, C)
			break
			]
		]

	return
	]


and STREAMTOQR(STREAM, Q) = valof

	[ until Endofs(STREAM) do

		unless PUTQR(Q, Gets(STREAM)) do resultis false
	resultis true
	]


and QFTOSTREAM(Q, STREAM) be

	[ until ISEMPTYQ(Q) do
		Puts(STREAM, GETQF(Q))
	return
	]


and GETNQE(NWDS) = valof

	[ let GOTIT = false
	let NQE = nil

	[ let MYOOPS = false
	NQE = Allocate(CZ, NWDS+(offset QE.DATA/16), true)

	test NQE eq 0

	ifso NWDS = NWDS rshift 1

	ifnot	[ GOTIT = true
		NQE>>QE.MAXLEN = NWDS+NWDS-1
		]

	] repeatuntil GOTIT % (NWDS ls DEFLEN)

	resultis GOTIT?
		NQE,
		Allocate(CZ, DEFLEN+(offset QE.DATA/16))
	]


and GETWFILE(FILE, QUEUE, ISPAUSE; numargs NA) = valof

	[ SFILE = FILE

	until Endofs(FILE) do
		[ if NA gr 2 then
			if ISPAUSE() then resultis true
		PUTNWQR(512, QUEUE, 0, MYRDVEC)
		]

	resultis false
	]


and MYRDVEC(DEST, SRC, N) = ReadBlock(SFILE, DEST, N)