<> <> <> <> <> DIRECTORY Basics USING [BITAND, BITNOT, BITOR, LowHalf], BasicTime USING [Now], FileNames USING [ConvertToSlashFormat, ResolveRelativePath], FS USING [BytesForPages, Close, Create, Delete, Error, ExpandName, nullOpenFile, Open, OpenFile, Read, SetByteCountAndCreatedTime, Write], IO USING [int, PutF, PutRope, STREAM], Jukebox, Rope USING [Equal, ROPE], VM USING [AddressForPageNumber, Allocate, Free, Interval, wordsPerPage]; JukeboxImpl: MONITOR LOCKS self.LOCK USING self: Jukebox.Handle IMPORTS Basics, BasicTime, FileNames, FS, IO, Rope, VM EXPORTS Jukebox SHARES Jukebox = BEGIN OPEN Jukebox; <<>> <> <<>> instances: PUBLIC LIST OF Jukebox.Handle _ NIL; <> FormatErrorMsg: Rope.ROPE = "Internal format error in jukebox."; OpenErrorMsg: Rope.ROPE = "No jukebox open."; BadTuneErrorMsg: Rope.ROPE = "Bad tune id."; BadTunePointerMsg: Rope.ROPE = "Bad tune pointer."; <> HeaderPageNumber: Jukebox.PageNumber = 1; FirstMapPageNumber: Jukebox.PageNumber = 2; <> Bits: ARRAY[0..15] OF CARDINAL = [1,2,4,10B,20B,40B,100B,200B,400B,1000B,2000B, 4000B,10000B,20000B,40000B,100000B]; BitsInUse: CARDINAL = 177777B; <> Error: PUBLIC ERROR[reason: ErrorCode, rope: Rope.ROPE] = CODE; MissingChirp: PUBLIC ERROR = CODE; EOF: PUBLIC ERROR = CODE; <> <<>> SHORT: PROC [l: LONG UNSPECIFIED] RETURNS [UNSPECIFIED] = { RETURN[Basics.LowHalf[l]]; }; <<>> <> CreateJukebox: PUBLIC PROC [name: Rope.ROPE, nPages: INT, nTunes: INT] = { space: VM.Interval; -- Holds jukebox disk block. locWindow: Jukebox.WindowOrigin; -- Used to initialize jukebox. h: LONG POINTER TO JukeboxHeader; m: LONG POINTER TO TuneBitMap; d: LONG POINTER TO TuneDescriptor; f: LONG POINTER TO FreeList; nChirps, nMaps: INT; firstChirp: INT; t1, t2: INTEGER; fullFName: Rope.ROPE; errorMsg: Rope.ROPE _ NIL; self: Jukebox.Handle _ NEW[Jukebox.JukeboxObject]; nMaps _ (nTunes + 16*bitMapSize - 1)/(16*bitMapSize); firstChirp _ 2 + nMaps + 2*nTunes; nChirps _ (nPages - firstChirp)/pagesPerChirp; IF nChirps < 1 THEN ERROR Error[TooManyTunes, "Too many tunes requested for jukebox size."]; name _ FileNames.ResolveRelativePath[name]; fullFName _ FS.ExpandName[name ! FS.Error => IF error.group = $user THEN { errorMsg _ error.explanation; CONTINUE; }].fullFName; IF errorMsg # NIL THEN ERROR Error[reason: NoJukebox, rope: errorMsg]; locWindow.file _ FS.nullOpenFile; locWindow.file _ FS.Open[name: fullFName, lock: $write, wDir: NIL ! FS.Error => { SELECT error.group FROM $lock => ERROR Error[reason: JukeboxOpen, rope: error.explanation]; ENDCASE => CONTINUE; }]; IF locWindow.file # FS.nullOpenFile THEN { FS.Close[locWindow.file]; ERROR Error[reason: JukeboxOpen, rope: "Cannot create jukebox: already exists"]; }; locWindow.file _ FS.Create[name: fullFName, pages: nPages, setKeep: TRUE, keep: 1, wDir: NIL]; FS.SetByteCountAndCreatedTime[file: locWindow.file, bytes: FS.BytesForPages[nPages], created: BasicTime.Now[]]; <> <> space _ VM.Allocate[count: 1]; h _ LOOPHOLE[VM.AddressForPageNumber[page: space.page]]; m _ LOOPHOLE[h]; d _ LOOPHOLE[h]; f _ LOOPHOLE[h]; <> h.magic _ magicJukeboxHeader; h.nTunes _ nTunes; h.nFreeChirps _ h.nChirps _ nChirps; h.freeListHead _ h.firstChirp _ firstChirp; h.tuneHWM _ -1; locWindow.base _ HeaderPageNumber; CopyOut[space, locWindow]; <> FOR i: INTEGER IN [0..bitMapSize) DO m.bits[i] _ 0 ENDLOOP; m.magic _ magicTuneBitMap; FOR i: INT IN [0..nMaps) DO locWindow.base _ FirstMapPageNumber + i; CopyOut[space, locWindow]; ENDLOOP; <> <> t1 _ SHORT[nMaps*bitMapSize*16 - nTunes]; t2 _ bitMapSize - t1/16; t1 _ t1 MOD 16; FOR i: INTEGER IN [t2..bitMapSize) DO m.bits[i] _ BitsInUse ENDLOOP; FOR i: INTEGER IN [16-t1..15] DO m.bits[t2-1] _ Basics.BITOR[m.bits[t2-1], Bits[i]]; ENDLOOP; CopyOut[space, locWindow]; <> d.magic _ magicTuneHeader; d.state _ available; d.size _ 0; FOR i: INTEGER IN [0..nHdrChirps) DO d.chirps[i] _ 0 ENDLOOP; d.indirectChirp _ 0; d.deepChirp _ 0; d.openCount _ 0; FOR i: INT IN [0..nTunes) DO d.tuneId _ locWindow.base _ 2*i + 2 + nMaps; CopyOut[space, locWindow]; ENDLOOP; <> f.magic _ magicFreeList; f.nChirps _ freeListSize; FOR i: INTEGER IN [0..freeListSize) DO f.chirps[i] _ firstChirp + pagesPerChirp*i; ENDLOOP; f.next _ f.chirps[freeListSize-1] + pagesPerChirp; locWindow.base _ firstChirp; CopyOut[space, locWindow]; FOR i: INT IN [1..(nChirps + freeListSize - 1)/freeListSize) DO FOR j: INTEGER IN [0..freeListSize) DO f.chirps[j] _ f.chirps[j] + freeListSize*pagesPerChirp; ENDLOOP; f.next _ f.next + freeListSize*pagesPerChirp; locWindow.base _ firstChirp + i*freeListSize*pagesPerChirp; CopyOut[space, locWindow]; ENDLOOP; <> <> f.nChirps _ SHORT[nChirps MOD freeListSize]; f.next _ 0; IF f.nChirps = 0 THEN f.nChirps _ freeListSize; CopyOut[space, locWindow]; VM.Free[space]; FS.Close[locWindow.file]; locWindow.file _ FS.nullOpenFile; }; DeleteJukebox: PUBLIC PROC [name: Rope.ROPE] = { fullFName: Rope.ROPE; errorMsg: Rope.ROPE _ NIL; name _ FileNames.ResolveRelativePath[name]; fullFName _ FS.ExpandName[name ! FS.Error => IF error.group = $user THEN { errorMsg _ error.explanation; CONTINUE; }].fullFName; IF errorMsg # NIL THEN ERROR Error[reason: NoJukebox, rope: errorMsg]; IF FindJukebox[fullFName] # NIL THEN ERROR Error[reason: JukeboxOpen, rope: "Cannot delete an open jukebox"]; FS.Delete[name: fullFName, wDir: NIL]; }; FindJukebox: PUBLIC PROC [name: Rope.ROPE] RETURNS [Jukebox.Handle] = { fullFName: Rope.ROPE; errorMsg: Rope.ROPE _ NIL; name _ FileNames.ResolveRelativePath[name]; fullFName _ FS.ExpandName[name ! FS.Error => IF error.group = $user THEN { errorMsg _ error.explanation; CONTINUE; }].fullFName; IF errorMsg # NIL THEN ERROR Error[reason: NoJukebox, rope: errorMsg]; fullFName _ FileNames.ConvertToSlashFormat[fullFName]; FOR l: LIST OF Jukebox.Handle _ instances, l.rest WHILE l # NIL DO IF Rope.Equal[l.first.jukeboxName, fullFName, FALSE] THEN RETURN[l.first]; ENDLOOP; RETURN[NIL]; }; OpenJukebox: PUBLIC PROC [name: Rope.ROPE] RETURNS [Jukebox.Handle] = { fullFName: Rope.ROPE; errorMsg: Rope.ROPE _ NIL; self: Jukebox.Handle _ NEW[Jukebox.JukeboxObject]; name _ FileNames.ResolveRelativePath[name]; fullFName _ FS.ExpandName[name ! FS.Error => IF error.group = $user THEN { errorMsg _ error.explanation; CONTINUE; }].fullFName; IF errorMsg # NIL THEN ERROR Error[reason: NoJukebox, rope: errorMsg]; fullFName _ FileNames.ConvertToSlashFormat[fullFName]; IF FindJukebox[fullFName] # NIL THEN ERROR Error[reason: JukeboxOpen, rope: "Jukebox already open"]; self.window.file _ FS.Open[name: fullFName, lock: $write, wDir: NIL ! FS.Error => { SELECT error.group FROM $user => ERROR Error[reason: NoJukebox, rope: error.explanation]; $lock => ERROR Error[reason: JukeboxOpen, rope: error.explanation]; ENDCASE => REJECT; }]; self.jukeboxName _ fullFName; OpenLocked[self]; RETURN[self]; }; OpenLocked: ENTRY PROC [self: Jukebox.Handle] = { ourhdr: LONG POINTER TO JukeboxHeader _ NIL; ourfree: LONG POINTER TO FreeList _ NIL; { ENABLE UNWIND => { IF ourhdr # NIL THEN VM.Free[self.hdrSpace]; IF ourfree # NIL THEN VM.Free[self.freeSpace]; }; <> self.hdrSpace _ VM.Allocate[count: 1]; self.freeSpace _ VM.Allocate[count: 1]; ourhdr _ LOOPHOLE[VM.AddressForPageNumber[self.hdrSpace.page]]; ourfree _ LOOPHOLE[VM.AddressForPageNumber[self.freeSpace.page]]; <> self.window.base _ HeaderPageNumber; CopyIn[self.hdrSpace, self.window]; IF ourhdr.magic # magicJukeboxHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; self.currentMapPageNumber _ FirstMapPageNumber; self.window.base _ self.currentFreeChirpNumber _ ourhdr.freeListHead; IF self.currentFreeChirpNumber # 0 THEN { CopyIn[self.freeSpace, self.window]; IF ourfree.magic # magicFreeList THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; } ELSE { ourfree.magic _ magicFreeList; ourfree.nChirps _ 0; ourfree.next _ 0; }; self.maxMapPageNumber _ FirstMapPageNumber + (ourhdr.nTunes/(16*bitMapSize)); self.firstDesPageNumber _ self.maxMapPageNumber + 1; self.openTuneHeaderList _ NIL; <> ourhdr.freeListHead _ ourfree.next; ourhdr.nFreeChirps _ ourhdr.nFreeChirps - ourfree.nChirps; self.window.base _ HeaderPageNumber; CopyOut[self.hdrSpace, self.window]; self.hdr _ ourhdr; self.free _ ourfree; instances _ CONS[self, instances]; }}; Info: PUBLIC ENTRY PROC [self: Jukebox.Handle] RETURNS [name: Rope.ROPE, nPages, nTunes: INT, tuneHWM: INT] = { ENABLE UNWIND => NULL; loc: Rope.ROPE; loc _ self.jukeboxName; IF self.hdr = NIL THEN RETURN[name: NIL, nPages: 0, nTunes: 0, tuneHWM: -1]; RETURN [name: loc, nPages: self.hdr.nChirps*pagesPerChirp, nTunes: self.hdr.nTunes, tuneHWM: self.hdr.tuneHWM]; }; CloseJukebox: PUBLIC ENTRY PROC [self: Jukebox.Handle] RETURNS [Jukebox.Handle] = { ENABLE UNWIND => NULL; IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg]; <> WHILE self.openTuneHeaderList # NIL DO self.openTuneHeaderList.openCount _ 1; InternalCloseTune[self, self.openTuneHeaderList, TRUE] ENDLOOP; <> self.hdr.nFreeChirps _ self.hdr.nFreeChirps + self.free.nChirps; IF self.free.nChirps # 0 THEN { self.free.next _ self.hdr.freeListHead; self.hdr.freeListHead _ self.window.base _ self.currentFreeChirpNumber; CopyOut[self.freeSpace, self.window]; }; self.window.base _ HeaderPageNumber; CopyOut[self.hdrSpace, self.window]; <> VM.Free[self.freeSpace]; self.free _ NIL; VM.Free[self.hdrSpace]; self.hdr _ NIL; FS.Close[self.window.file]; self.window.file _ FS.nullOpenFile; instances _ DRemove[ref: self, list: instances]; RETURN[NIL]; }; DRemove: INTERNAL PROC [ref: Jukebox.Handle, list: LIST OF Jukebox.Handle] RETURNS[LIST OF Jukebox.Handle] = { l, l1: LIST OF Jukebox.Handle _ NIL; l _ list; UNTIL l = NIL DO IF l.first = ref THEN { IF l1 = NIL THEN RETURN[l.rest]; -- ref was first object on list l1.rest _ l.rest; RETURN[list]; }; l1 _ l; l _ l.rest; ENDLOOP; RETURN [list]; }; -- of Dremove; CreateTune: PUBLIC ENTRY PROC [self: Jukebox.Handle, tuneId: Jukebox.TuneID _ Jukebox.newTuneID] RETURNS [Tune] = { <> ENABLE UNWIND => NULL; tune: Tune _ NIL; word, bit: INTEGER; mapPage: INT; mapSpace: VM.Interval; map: LONG POINTER TO TuneBitMap _ NIL; tuneSpace: VM.Interval; {ENABLE UNWIND => { IF map # NIL THEN VM.Free[mapSpace]; IF tune # NIL THEN VM.Free[tuneSpace]; }; IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg]; <> mapSpace _ VM.Allocate[count: 1]; map _ LOOPHOLE[VM.AddressForPageNumber[mapSpace.page]]; tuneSpace _ VM.Allocate[count: 1]; tune _ LOOPHOLE[VM.AddressForPageNumber[tuneSpace.page]]; <> IF tuneId >= 0 THEN { IF tuneId >= self.hdr.nTunes THEN ERROR Error[BadTune, BadTuneErrorMsg]; IF searchTuneCache[self, tuneId] # NIL THEN ERROR Error[TuneLocked, "Tune is locked."]; self.window.base _ desPage[self, tuneId]; CopyIn[space: tuneSpace, window: self.window]; IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; tune.tuneId _ tuneId; tune.space _ tuneSpace; IF tune.state = inUse THEN killTune[self: self, tune: tune, updateMap: FALSE] ELSE { <> self.window.base _ FirstMapPageNumber + tuneId/(bitMapSize*16); CopyIn[space: mapSpace, window: self.window]; IF map.magic # magicTuneBitMap THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; word _ SHORT[tuneId MOD (bitMapSize*16)]; bit _ word MOD 16; word _ word/16; map.bits[word] _ Basics.BITOR[map.bits[word], Bits[bit]]; CopyOut[mapSpace, self.window]; }; } ELSE { <> mapPage _ self.window.base _ self.currentMapPageNumber; CopyIn[mapSpace, self.window]; IF map.magic # magicTuneBitMap THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; WHILE TRUE DO FOR i: INTEGER IN [0..bitMapSize) DO IF map.bits[i] # 177777B THEN { FOR j: INTEGER IN [0..16) DO IF Basics.BITAND[map.bits[i], Bits[j]] = 0 THEN { <> map.bits[i] _ Basics.BITOR[map.bits[i], Bits[j]]; self.window.base _ mapPage; CopyOut[mapSpace, self.window]; tuneId _ (mapPage-FirstMapPageNumber)*16*bitMapSize + i*16 + j; self.window.base _ desPage[self, tuneId]; CopyIn[tuneSpace, self.window]; IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; IF tune.state = available THEN GOTO gotTune; }; ENDLOOP; }; ENDLOOP; mapPage _ mapPage+1; IF mapPage > self.maxMapPageNumber THEN mapPage _ FirstMapPageNumber; IF mapPage = self.currentMapPageNumber THEN ERROR Error[NoTunesAvailable, "All tune headers in use."]; self.window.base _ mapPage; CopyIn[mapSpace, self.window]; IF map.magic # magicTuneBitMap THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; REPEAT gotTune => self.currentMapPageNumber _ mapPage; ENDLOOP; }; <> IF tuneId > self.hdr.tuneHWM THEN { self.hdr.tuneHWM _ tuneId; self.window.base _ HeaderPageNumber; CopyOut[self.hdrSpace, self.window] }; tune.state _ inUse; tune.runData _ NIL; tune.createDate _ tune.appendDate _ tune.playDate _ BasicTime.Now[]; tune.size _ 0; FOR i: INTEGER IN [0..nHdrChirps) DO tune.chirps[i] _ 0; ENDLOOP; tune.indirectChirp _ tune.deepChirp _ 0; tune.tuneId _ tuneId; tune.openCount _ 1; tune.next _ self.openTuneHeaderList; tune.space _ tuneSpace; tune.writable _ TRUE; tune.kill _ FALSE; tune.deepCache _ tune.indirectCache _ NIL; self.window.base _ desPage[self, tuneId]; CopyOut[tuneSpace, self.window]; self.openTuneHeaderList _ tune; RETURN [tune]; }}; DeleteTune: PUBLIC ENTRY PROC [self: Jukebox.Handle, tuneId: Jukebox.TuneID] = { ENABLE UNWIND => NULL; tuneSpace: VM.Interval; tune: Tune _ NIL; {ENABLE UNWIND => { IF tune # NIL THEN VM.Free[tuneSpace]; }; IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg]; IF (tuneId >= self.hdr.nTunes) OR (tuneId < 0) THEN ERROR Error[BadTune, BadTuneErrorMsg]; tuneSpace _ VM.Allocate[count: 1]; <> tune _ searchTuneCache[self, tuneId]; IF tune = NIL THEN { <> tune _ LOOPHOLE[VM.AddressForPageNumber[tuneSpace.page]]; self.window.base _ desPage[self, tuneId]; CopyIn[tuneSpace, self.window]; tune.tuneId _ tuneId; tune.openCount _ 0; tune.next _ NIL; tune.space _ tuneSpace; tune.writable _ tune.kill _ FALSE; }; <> IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; <> IF tune.state # inUse THEN ERROR Error[BadTune, BadTuneErrorMsg]; <> tune.kill _ TRUE; IF tune.openCount = 0 THEN killTune[self, tune]; VM.Free[tuneSpace]; }}; killTune: INTERNAL PROC [self: Jukebox.Handle, tune: Tune, updateMap: BOOL _ TRUE] = { <<>> <> mapSpace: VM.Interval; map: LONG POINTER TO TuneBitMap _ NIL; deepSpace: VM.Interval; deepList: LONG POINTER TO ChirpList _ NIL; word, bit: INTEGER; {ENABLE UNWIND => IF map # NIL THEN VM.Free[mapSpace]; <> tune.state _ available; tune.openCount _ 0; tune.kill _ FALSE; self.window.base _ desPage[self, tune.tuneId]; CopyOut[tune.space, self.window]; <> FOR i: INTEGER IN [0..nHdrChirps) DO IF tune.chirps[i] # 0 THEN FreeChirp[self, tune.chirps[i]]; ENDLOOP; IF tune.indirectChirp # 0 THEN FreeIndirect[self, tune.indirectChirp]; IF tune.deepChirp # 0 THEN { ENABLE UNWIND => IF deepList # NIL THEN VM.Free[deepSpace]; deepSpace _ VM.Allocate[count: pagesPerChirp]; deepList _ LOOPHOLE[VM.AddressForPageNumber[deepSpace.page]]; self.window.base _ tune.deepChirp; CopyIn[space: deepSpace, window: self.window]; IF deepList.magic # magicDeepList THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; FOR i: INTEGER IN [0..chirpListSize) DO IF deepList.chirps[i] # 0 THEN FreeIndirect[self, deepList.chirps[i]]; ENDLOOP; FreeChirp[self, tune.deepChirp]; VM.Free[deepSpace]; }; IF NOT updateMap THEN RETURN; <> mapSpace _ VM.Allocate[count: 1]; map _ LOOPHOLE[VM.AddressForPageNumber[mapSpace.page]]; self.window.base _ FirstMapPageNumber + tune.tuneId/(bitMapSize*16); CopyIn[mapSpace, self.window]; word _ SHORT[tune.tuneId MOD (bitMapSize*16)]; bit _ word MOD 16; word _ word/16; map.bits[word] _ Basics.BITAND[map.bits[word], Basics.BITNOT[Bits[bit]]]; CopyOut[mapSpace, self.window]; VM.Free[mapSpace]; }}; OpenTune: PUBLIC ENTRY PROC [self: Jukebox.Handle, tuneId: TuneID, write: BOOL] RETURNS [Tune] = { ENABLE UNWIND => NULL; tune: Tune _ NIL; tuneSpace: VM.Interval; {ENABLE UNWIND => { IF tune # NIL THEN VM.Free[tuneSpace]; }; IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg]; <> IF (tuneId >= self.hdr.nTunes) OR (tuneId < 0) THEN ERROR Error[BadTune, BadTuneErrorMsg]; tuneSpace _ VM.Allocate[count: 1]; <> tune _ searchTuneCache[self, tuneId]; IF tune # NIL THEN { <> IF tune.kill THEN ERROR Error[BadTune, BadTuneErrorMsg]; IF tune.writable OR write THEN ERROR Error[TuneLocked, "Tune is locked."]; tune.openCount _ tune.openCount + 1; VM.Free[tuneSpace]; tune.runData _ NIL; RETURN [tune]; }; <> tune _ LOOPHOLE[VM.AddressForPageNumber[tuneSpace.page]]; self.window.base _ desPage[self, tuneId]; CopyIn[tuneSpace, self.window]; IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; <> IF tune.state # inUse THEN ERROR Error[BadTune, BadTuneErrorMsg]; tune.tuneId _ tuneId; tune.openCount _ 1; tune.next _ self.openTuneHeaderList; tune.space _ tuneSpace; tune.writable _ write; tune.kill _ FALSE; tune.deepCache _ tune.indirectCache _ NIL; self.openTuneHeaderList _ tune; tune.runData _ NIL; RETURN [tune]; }}; CloseTune: PUBLIC ENTRY PROC [self: Jukebox.Handle, tune: Tune] = { ENABLE UNWIND => NULL; InternalCloseTune[self, tune, TRUE]; }; ArchiveCloseTune: PUBLIC ENTRY PROC [self: Jukebox.Handle, tune: Tune] = { <> ENABLE UNWIND => NULL; InternalCloseTune[self, tune, FALSE]; }; InternalCloseTune: INTERNAL PROC [self: Jukebox.Handle, tune: Tune, updateDates: BOOLEAN] = { tuneptr: LONG POINTER TO Tune; tuneSpace: VM.Interval; <<>> <> IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg]; IF tune.magic # magicTuneHeader THEN ERROR Error[BadTune, BadTuneErrorMsg]; <> tune.openCount _ tune.openCount - 1; IF tune.openCount = 0 THEN { IF updateDates THEN {IF tune.writable THEN tune.appendDate _ BasicTime.Now[] ELSE tune.playDate _ BasicTime.Now[]}; IF tune.deepCache # NIL THEN { IF tune.deepCache.dirty THEN { self.window.base _ tune.deepChirp; CopyOut[space: tune.deepSpace, window: self.window]; }; VM.Free[tune.deepSpace]; tune.deepCache _ NIL; }; IF tune.indirectCache # NIL THEN { IF tune.indirectCache.dirty THEN { self.window.base _ tune.indirectCache.page; CopyOut[space: tune.indirectSpace, window: self.window]; }; VM.Free[tune.indirectSpace]; tune.indirectCache _ NIL; }; tuneSpace _ tune.space; tuneptr _ @self.openTuneHeaderList; WHILE tuneptr^ # tune DO IF tuneptr^ = NIL THEN ERROR Error[Bug, "Unexpected tune list end."]; tuneptr _ @(tuneptr^.next); ENDLOOP; tuneptr^ _ tune.next; <> IF tune.kill THEN killTune[self, tune] ELSE { self.window.base _ desPage[self, tune.tuneId]; CopyOut[tuneSpace, self.window]; }; tune.magic _ 0; IF tune.runData#NIL THEN { VM.Free[tune.runSpace]; tune.runData _ NIL; }; VM.Free[tuneSpace]; }; }; AllocChirp: ENTRY PROC [self: Jukebox.Handle] RETURNS [Jukebox.PageNumber] = { <> ENABLE UNWIND => NULL; chirp: Jukebox.PageNumber; WHILE self.free.nChirps = 0 DO IF self.free.next = 0 THEN ERROR Error[NoFreeChirps, "No free chirps left."]; self.window.base _ self.currentFreeChirpNumber _ self.free.next; CopyIn[space: self.freeSpace, window: self.window]; IF self.free.magic # magicFreeList THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; self.hdr.freeListHead _ self.free.next; self.hdr.nFreeChirps _ self.hdr.nFreeChirps - self.free.nChirps; self.window.base _ HeaderPageNumber; CopyOut[space: self.hdrSpace, window: self.window]; ENDLOOP; self.free.nChirps _ self.free.nChirps-1; chirp _ self.free.chirps[self.free.nChirps]; RETURN [chirp]; }; AllocateIndirect: PROC [self: Jukebox.Handle, magic: INTEGER] RETURNS [Jukebox.PageNumber] = { <> page: Jukebox.PageNumber; space: VM.Interval; chirpList: LONG POINTER TO ChirpList; {ENABLE UNWIND => IF chirpList # NIL THEN VM.Free[space]; page _ AllocChirp[self]; space _ VM.Allocate[count: pagesPerChirp]; chirpList _ LOOPHOLE[VM.AddressForPageNumber[space.page]]; chirpList.magic _ magic; FOR j: INTEGER IN [0..chirpListSize) DO chirpList.chirps[j] _ 0 ENDLOOP; chirpList.dirty _ FALSE; self.window.base _ page; CopyOut[space: space, window: self.window]; VM.Free[space]; RETURN [page]; }}; FreeChirp: PROC [self: Jukebox.Handle, chirp: Jukebox.PageNumber] = { <> <> ENABLE UNWIND => NULL; <> IF self.free.nChirps = freeListSize THEN { self.window.base _ self.currentFreeChirpNumber; CopyOut[space: self.freeSpace, window: self.window]; self.free.next _ self.currentFreeChirpNumber; self.free.nChirps _ 0; <> self.hdr.freeListHead _ self.free.next; self.hdr.nFreeChirps _ self.hdr.nFreeChirps + freeListSize; self.window.base _ HeaderPageNumber; CopyOut[space: self.hdrSpace, window: self.window]; }; IF self.free.nChirps = 0 THEN self.currentFreeChirpNumber _ chirp; self.free.chirps[self.free.nChirps] _ chirp; self.free.nChirps _ self.free.nChirps+1; }; FreeIndirect: PROC [self: Jukebox.Handle, page: Jukebox.PageNumber] = { <> indirectSpace: VM.Interval; chirpList: LONG POINTER TO ChirpList _ NIL; {ENABLE UNWIND => IF chirpList # NIL THEN VM.Free[indirectSpace]; indirectSpace _ VM.Allocate[count: pagesPerChirp]; chirpList _ LOOPHOLE[VM.AddressForPageNumber[indirectSpace.page]]; self.window.base _ page; CopyIn[space: indirectSpace, window: self.window]; IF chirpList.magic # magicChirpList THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; FOR i: INTEGER IN [0..chirpListSize) DO IF chirpList.chirps[i] # 0 THEN FreeChirp[self, chirpList.chirps[i]]; ENDLOOP; FreeChirp[self, page]; VM.Free[indirectSpace]; }}; FindChirp: PUBLIC PROC [self: Jukebox.Handle, tune: Tune, chirp: INT, signalMissingChirp: BOOL _ FALSE, signalEOF: BOOL _ FALSE] RETURNS [Jukebox.WindowOrigin] = { <> index, indirect: INTEGER; chirpWindow: Jukebox.WindowOrigin; IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg]; IF tune.magic # magicTuneHeader THEN ERROR Error[BadTunePointer, BadTunePointerMsg]; <> IF (tune.size <= chirp) THEN { IF signalEOF THEN ERROR EOF[]; tune.size _ chirp+1; }; chirpWindow.file _ self.window.file; IF chirp < nHdrChirps THEN { index _ SHORT[chirp]; IF tune.chirps[index] = 0 THEN { IF signalMissingChirp THEN ERROR MissingChirp[]; tune.chirps[index] _ AllocChirp[self]; }; chirpWindow.base _ tune.chirps[index]; RETURN [chirpWindow]; }; chirp _ chirp - nHdrChirps; index _ SHORT[chirp MOD chirpListSize]; chirp _ chirp/chirpListSize; IF chirp > chirpListSize THEN ERROR Error[TuneTooLarge, "Can't have tune that large."]; indirect _ SHORT[chirp]; IF (indirect # tune.indirect) OR (tune.indirectCache = NIL) THEN { <> IF tune.indirectCache = NIL THEN { tune.indirectSpace _ VM.Allocate[count: pagesPerChirp]; tune.indirectCache _ LOOPHOLE[VM.AddressForPageNumber[tune.indirectSpace.page]]; tune.indirectCache.dirty _ FALSE; tune.indirect _ -1; }; IF tune.indirectCache.dirty THEN { tune.indirectCache.dirty _ FALSE; self.window.base _ tune.indirectCache.page; CopyOut[space: tune.indirectSpace, window: self.window]; }; IF indirect = 0 THEN { IF tune.indirectChirp = 0 THEN { IF signalMissingChirp THEN ERROR MissingChirp[]; tune.indirectChirp _ AllocateIndirect[self, magicChirpList]; }; self.window.base _ tune.indirectChirp; } ELSE { IF (tune.deepCache = NIL) THEN { <> IF tune.deepChirp = 0 THEN { IF signalMissingChirp THEN ERROR MissingChirp[]; tune.deepChirp _ AllocateIndirect[self, magicDeepList]; }; self.window.base _ tune.deepChirp; tune.deepSpace _ VM.Allocate[count: pagesPerChirp]; tune.deepCache _ LOOPHOLE[VM.AddressForPageNumber[tune.deepSpace.page]]; CopyIn[space: tune.deepSpace, window: self.window]; }; IF tune.deepCache.magic # magicDeepList THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; IF tune.deepCache.chirps[indirect-1] = 0 THEN { IF signalMissingChirp THEN ERROR MissingChirp[]; tune.deepCache.chirps[indirect-1] _ AllocateIndirect[self, magicChirpList]; tune.deepCache.dirty _ TRUE; }; self.window.base _ tune.deepCache.chirps[indirect-1]; }; CopyIn[space: tune.indirectSpace, window: self.window]; tune.indirectCache.page _ self.window.base; tune.indirect _ indirect; }; IF tune.indirectCache.magic # magicChirpList THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; IF tune.indirectCache.chirps[index] = 0 THEN { IF signalMissingChirp THEN ERROR MissingChirp[]; tune.indirectCache.chirps[index] _ AllocChirp[self]; tune.indirectCache.dirty _ TRUE; }; chirpWindow.base _ tune.indirectCache.chirps[index]; RETURN [chirpWindow]; }; <> FindClientSpace: PUBLIC PROC [self: Jukebox.Handle, tuneId: INT] RETURNS [clientWindow: Jukebox.WindowOrigin] = { IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg]; IF (tuneId >= self.hdr.nTunes) OR (tuneId < 0) THEN ERROR Error[BadTune, BadTuneErrorMsg]; clientWindow.file _ self.window.file; clientWindow.base _ desPage[self, tuneId] + 1; }; desPage: PROC [self: Jukebox.Handle, tuneId: INT] RETURNS [INT] = { <<>> <> RETURN[self.firstDesPageNumber + 2*tuneId]; }; searchTuneCache: INTERNAL PROC [self: Jukebox.Handle, tuneId: INT] RETURNS [Tune] = { <<>> <> tune: Tune; tune _ self.openTuneHeaderList; WHILE tune # NIL DO IF tune.tuneId = tuneId THEN RETURN [tune]; tune _ tune.next; ENDLOOP; RETURN [NIL]; }; Scavenge: PUBLIC ENTRY PROC [self: Jukebox.Handle, stream: IO.STREAM, makeFixes: BOOL] RETURNS [nFree: INT, recovered: INT] = { <> <> ENABLE UNWIND => NULL; varArray: TYPE = ARRAY [0..0) OF INT; chirpMap: LONG POINTER TO varArray _ NIL; chirpSpace: VM.Interval; tuneId: INT; tuneSpace, indirectSpace, deepSpace: VM.Interval; tune: Tune _ NIL; indirect: LONG POINTER TO ChirpList _ NIL; deep: LONG POINTER TO ChirpList _ NIL; index: INT; oldNFree, newNFree: INT; badTunes: BOOL _ FALSE; unused: INT _ -1; tuneHWM: INT _ -1; gcIndex: PROC [chirp: INT] RETURNS [INT] = { <> result: INT; chirp _ chirp-self.hdr.firstChirp; result _ chirp/pagesPerChirp; IF (result >= self.hdr.nChirps) OR (result < 0) THEN ERROR Error[BadChirpPointer, "Bad chirp pointer."]; IF (result*pagesPerChirp) # chirp THEN ERROR Error[BadChirpPointer, "Bad chirp pointer."]; RETURN[result]; }; gcIndirect: PROC [tuneId: INT, chirp: INT, magic: INTEGER] = { <> index: INT; IF chirp = 0 THEN RETURN; self.window.base _ chirp; CopyIn[space: indirectSpace, window: self.window]; IF indirect.magic # magic THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; FOR i:INTEGER IN [0..chirpListSize) DO IF indirect.chirps[i] # 0 THEN { index _ gcIndex[indirect.chirps[i]]; IF chirpMap[index] = unused THEN chirpMap[index] _ tuneId ELSE { IO.PutRope[stream, "Indirect/deep pointer is duplicate:\n"]; IO.PutF[stream, " Chirp: %d, 1st tune: %d, 2nd tune: %d.\n", IO.int[indirect.chirps[i]], IO.int[chirpMap[index]], IO.int[tuneId]]; IF makeFixes THEN { IO.PutRope[stream, " Chirp deleted from 2nd tune.\n"]; indirect.chirps[i] _ 0; CopyOut[space: indirectSpace, window: self.window]; }; }; }; ENDLOOP; }; {ENABLE UNWIND => { IF indirect # NIL THEN VM.Free[indirectSpace]; IF deep # NIL THEN VM.Free[deepSpace]; IF tune # NIL THEN VM.Free[tuneSpace]; IF chirpMap # NIL THEN VM.Free[chirpSpace]; }; <
> IF self.hdr = NIL THEN ERROR Error[NoJukebox, OpenErrorMsg]; IF self.openTuneHeaderList # NIL THEN ERROR Error[TunesOpen, "Can't scavenge: tune(s) open"]; <> chirpSpace _ VM.Allocate[count: ((self.hdr.nChirps * SIZE[INT]) / VM.wordsPerPage) + 1]; chirpMap _ LOOPHOLE[VM.AddressForPageNumber[chirpSpace.page]]; FOR i: INT IN [0..self.hdr.nChirps) DO chirpMap[i] _ unused ENDLOOP; <> tuneSpace _ VM.Allocate[count: 1]; tune _ LOOPHOLE[VM.AddressForPageNumber[tuneSpace.page]]; indirectSpace _ VM.Allocate[count: pagesPerChirp]; indirect _ LOOPHOLE[VM.AddressForPageNumber[indirectSpace.page]]; deepSpace _ VM.Allocate[count: pagesPerChirp]; deep _ LOOPHOLE[VM.AddressForPageNumber[deepSpace.page]]; FOR tuneId IN [0..self.hdr.nTunes) DO <> ENABLE { Error => { IO.PutRope[stream, rope]; IO.PutF[stream, " (in tune %d)\n", IO.int[tuneId]]; IF makeFixes THEN { IO.PutRope[stream, " Tune is being deleted"]; IO.PutRope[stream, " (some free chirps may not be recovered).\n"]; tune.magic _ magicTuneHeader; tune.state _ available; self.window.base _ desPage[self, tuneId]; CopyOut[space: tuneSpace, window: self.window]; } ELSE badTunes _ TRUE; CONTINUE; }; ANY => { IO.PutF[stream, "Unknown error occurred in scavenging tune %d.\n", IO.int[tuneId]]; IF makeFixes THEN { IO.PutRope[stream, " Tune is being deleted"]; IO.PutRope[stream, " (some free chirps may not be recovered).\n"]; tune.magic _ magicTuneHeader; tune.state _ available; self.window.base _ desPage[self, tuneId]; CopyOut[space: tuneSpace, window: self.window]; } ELSE badTunes _ TRUE; CONTINUE; }; }; self.window.base _ desPage[self, tuneId]; CopyIn[space: tuneSpace, window: self.window]; IF tune.magic # magicTuneHeader THEN ERROR Error[JukeboxFormat, FormatErrorMsg]; IF tune.state = available THEN LOOP ELSE tuneHWM _ tuneId; <> FOR i:INTEGER IN [0..nHdrChirps) DO IF tune.chirps[i] # 0 THEN { index _ gcIndex[tune.chirps[i]]; IF chirpMap[index] = unused THEN chirpMap[index] _ tuneId ELSE { IO.PutRope[stream, "Top level pointer is duplicate:\n"]; IO.PutF[stream, " Chirp: %d, 1st tune: %d, 2nd tune: %d.\n", IO.int[tune.chirps[i]], IO.int[chirpMap[index]], IO.int[tuneId]]; IF makeFixes THEN { IO.PutRope[stream, " Chirp deleted from 2nd tune.\n"]; tune.chirps[i] _ 0; CopyOut[space: tuneSpace, window: self.window]; }; }; }; ENDLOOP; <> IF tune.indirectChirp # 0 THEN { index _ gcIndex[tune.indirectChirp]; IF chirpMap[index] = unused THEN chirpMap[index] _ tuneId ELSE { IO.PutRope[stream, "Tune.indirectChirp is duplicate:\n"]; IO.PutF[stream, " Chirp: %d, 1st tune: %d, 2nd tune: %d.\n", IO.int[tune.indirectChirp], IO.int[chirpMap[index]], IO.int[tuneId]]; IF makeFixes THEN { IO.PutRope[stream, " Chirp deleted from 2nd tune.\n"]; tune.indirectChirp _ 0; self.window.base _ desPage[self, tuneId]; CopyOut[space: tuneSpace, window: self.window]; }; }; gcIndirect[tuneId, tune.indirectChirp, magicChirpList]; }; <> IF tune.deepChirp # 0 THEN { index _ gcIndex[tune.deepChirp]; IF chirpMap[index] = unused THEN chirpMap[index] _ tuneId ELSE { IO.PutRope[stream, "Tune.deepChirp is duplicate:\n"]; IO.PutF[stream, " Chirp: %d, 1st tune: %d, 2nd tune: %d.\n", IO.int[tune.deepChirp], IO.int[chirpMap[index]], IO.int[tuneId]]; IF makeFixes THEN { IO.PutRope[stream, " Chirp deleted from 2nd tune.\n"]; tune.deepChirp _ 0; self.window.base _ desPage[self, tuneId]; CopyOut[space: tuneSpace, window: self.window]; }; }; gcIndirect[tuneId, tune.deepChirp, magicDeepList]; self.window.base _ tune.deepChirp; CopyIn[space: deepSpace, window: self.window]; FOR i: INTEGER IN [0..chirpListSize) DO gcIndirect[tuneId, deep.chirps[i], magicChirpList] ENDLOOP; }; ENDLOOP; <> IF badTunes THEN { IO.PutRope[stream, "Bad tunes weren't deleted, so can't scavenge free list.\n"]; RETURN [nFree: self.hdr.nFreeChirps+self.free.nChirps, recovered: 0]; }; <> oldNFree _ 0; newNFree _ 0; DO FOR i:INTEGER IN [0..self.free.nChirps) DO oldNFree _ oldNFree + 1; index _ gcIndex[self.free.chirps[i]]; IF chirpMap[index] # unused THEN { IO.PutRope[stream, "Free list chirp is duplicate:\n"]; IO.PutF[stream, " Chirp: %d, Tune: %d.\n", IO.int[self.free.chirps[i]], IO.int[chirpMap[index]]]; }; ENDLOOP; IF self.free.next = 0 THEN EXIT; self.currentFreeChirpNumber _ self.window.base _ self.free.next; CopyIn[space: self.freeSpace, window: self.window]; ENDLOOP; <> self.free.next _ 0; self.free.nChirps _ 0; self.currentFreeChirpNumber _ 0; self.hdr.nFreeChirps _ 0; FOR i:INT IN [0..self.hdr.nChirps) DO IF chirpMap[i] = unused THEN { newNFree _ newNFree + 1; FreeChirp[self, self.hdr.firstChirp + i*pagesPerChirp]; }; ENDLOOP; <> IF tuneHWM # self.hdr.tuneHWM THEN { self.hdr.tuneHWM _ tuneHWM; self.window.base _ HeaderPageNumber; CopyOut[self.hdrSpace, self.window] }; <> VM.Free[tuneSpace]; VM.Free[indirectSpace]; VM.Free[deepSpace]; chirpMap _ NIL; VM.Free[chirpSpace]; RETURN[nFree: newNFree, recovered: newNFree - oldNFree]; }}; CopyIn: PROC [space: VM.Interval, window: Jukebox.WindowOrigin] = { FS.Read[file: window.file, from: window.base, nPages: space.count, to: VM.AddressForPageNumber[space.page]]; }; CopyOut: PROC [space: VM.Interval, window: Jukebox.WindowOrigin] = { FS.Write[file: window.file, to: window.base, nPages: space.count, from: VM.AddressForPageNumber[space.page]]; }; END. <> <> <> <> <> <> <> <<>>