Skip to content

Instantly share code, notes, and snippets.

@avdgrinten
Last active April 26, 2018 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save avdgrinten/21d0f77c666707e5b9116b4b762f814a to your computer and use it in GitHub Desktop.
Save avdgrinten/21d0f77c666707e5b9116b4b762f814a to your computer and use it in GitHub Desktop.
.kor language examples
/* This file is part of the Managarm operating system.
* Copyright (C) 2007, 2008, 2009 Alexander van der Grinten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
using Korona;
namespace Managarm.Drivers.X86.Fdc;
class DriveState {
private Controller controller;
private ubyte driveNumber;
// must only be modified by the processing thread
private boolean motorState;
// waits some seconds and then disables the motor
private Tasking.Timer motorTimer;
public constructor(Controller controller, ubyte number) {
this.controller = controller;
this.driveNumber = number;
}
public void updateMotor() {
if(motorTimer != null && motorTimer.activated())
motorTimer.disable();
motorTimer = new Tasking.Timer(delegateof disableMotor());
motorTimer.setMillis(5000);
motorTimer.enable();
}
// called by the timer to disable the motor
private void disableMotor() {
controller.postCommand(new CmdKillMotor(driveNumber), 0);
}
}
class Controller {
public static const uint ACTION_READ = 1;
public static const uint ACTION_WRITE = 2;
// floppy disk controller port offsets
public static const ubyte REG_DOR = 2; // digital output register
public static const ubyte REG_DSR = 4; // data rate select register
public static const ubyte REG_MSR = 4; // main status register
public static const ubyte REG_CCR = 7; // configuration control register
public static const ubyte REG_DATA = 5; // data register
// digital output register constants
public static const ubyte DOR_NORESET = 4;
public static const ubyte DOR_DMAGATE = 8;
public static const ubyte DOR_MOTOR0 = 16;
public static const ubyte DOR_MOTOR1 = 32;
public static const ubyte DOR_MOTOR2 = 64;
public static const ubyte DOR_MOTOR3 = 128;
// configuration control register constants
public static const ubyte CCR_RATE_500K = 0;
// main status register constants
public static const ubyte MSR_RQM = 128;
// status register constants
public static const ubyte ST0_INT_CODE = 0xC0;
public static const ubyte ST0_SEEK_END = 32;
// command constants
public static const ubyte CMD_SPECIFY = 3;
public static const ubyte CMD_SENSE_INT = 8;
public static const ubyte CMD_RECALIBRATE = 7;
public static const ubyte CMD_SEEK = 15;
public static const ubyte CMD_WRITE_SECTOR = 5;
public static const ubyte CMD_READ_SECTOR = 6;
// flags that change command behaviour
public static const ubyte CMDFLAG_MFM = 64; // double density (default)
// drives that are managed by this controller object
private ?DriveState drives;
// stores the commands that will be executed next
private $MinimumHeap[Command] commandQueue;
// event that is raised when a command arrives
private Tasking.Event commandEvent;
// proctects the command queue from concurrent access
private Tasking.Semaphore commandLock;
private uint controllerIo;
private uint controllerIrq;
// current drive that is selected
// must only be modified by the processing thread
private ubyte currentDrive;
public constructor(uint ctrlio, uint ctrlirq) {
this.controllerIo = ctrlio;
this.controllerIrq = ctrlirq;
// create the command queue
commandQueue = new $MinimumHeap[Command]
(delegateof Command.comparePriority(Command, Command));
commandLock = new Tasking.Semaphore();
commandEvent = new Tasking.Event();
// create the drive state structures
drives = new DriveState[4];
drives[0] = new DriveState(this, 0);
drives[1] = new DriveState(this, 1);
drives[2] = new DriveState(this, 2);
drives[3] = new DriveState(this, 3);
Tasking.Thread processor = new Tasking.Thread(delegateof process());
processor.startup();
}
public void postCommand(Command command, uint priority) {
command.priority = priority; // set the priority value
commandLock.acquire();
commandQueue.insert(command);
commandEvent.fire();
commandLock.release();
}
public void process() {
// initialize the fdc
resetController();
def Tasking.Listener listener = commandEvent.listen();
while(true) {
commandLock.acquire();
listener.reset();
Command command = commandQueue.fetch();
commandLock.release();
// if there is a command we execute it
// otherwise we sleep until a command arrives
if(command != null) {
execute(command);
}else{
listener.waitfor();
}
}
}
// processes a single command
// must only be called by the processing thread
private void execute(Command command) {
if(command instanceof CmdKillMotor) {
CmdKillMotor cmdobject = cast[CmdKillMotor]command;
shutdownMotor(cmdobject.driveNumber);
}else if(command instanceof CmdRwSector) {
CmdRwSector cmdobject = cast[CmdRwSector]command;
readWriteSector(cmdobject.action, cmdobject.driveNumber,
cmdobject.sectorNumber, cmdobject.buffer);
// let the finish event fire
cmdobject.currentState = CmdRwSector.STATE_SUCCESS;
cmdobject.stateEvent.fire();
}else{
System.errOut.writeLn("Illegal command for execute()");
}
}
// must only be called by the processing thread
private void shutdownMotor(ubyte drivenum) {
drives[cast[uint]drivenum].motorState = false;
updateDrive();
}
// must only be called by the processing thread
private void readWriteSector(uint action, ubyte drivenum,
uint secnum, ?ubyte buffer) {
selectDrive(drivenum);
// program the data rate
Managarm.ManaIo.writeIo8(controllerIo, REG_CCR, CCR_RATE_500K);
// calculate the chs values of the sector
ubyte chs_sector = cast[ubyte](secnum % 18 + 1);
ubyte chs_cylinder = cast[ubyte](secnum / 18 / 2);
ubyte chs_head = cast[ubyte](secnum / 18 % 2);
// execute a recalibrate command (try it 5 times)
uint recal_tries = 5;
while(recal_tries != 0 && recalibrate() == false)
recal_tries--;
if(recal_tries == 0)
System.stdOut.writeLn("recalibrate() failed!");
// execute a seek command (try it 5 times)
// TODO: do we have to send this command?
uint seek_tries = 5;
while(seek_tries != 0 && seek(chs_cylinder) == false)
seek_tries--;
if(seek_tries == 0)
System.stdOut.writeLn("seek() failed!");
setupDma();
// send the read sectors command
// note that we operate in double density (mfm) mode
resetIrq();
if(action == ACTION_READ) {
sendCommand(CMD_READ_SECTOR | CMDFLAG_MFM);
}else{
sendCommand(CMD_WRITE_SECTOR | CMDFLAG_MFM);
}
sendCommand((chs_head << 2) | drivenum);
sendCommand(chs_cylinder);
sendCommand(chs_head);
sendCommand(chs_sector);
// sets the length of one sector to 512 bytes
sendCommand(2);
// number of sectors this command operates on
sendCommand(18);
// length of gap3. must be 27 for 3 1/2" disks
sendCommand(27);
// has no meaning when sector size == 512 but intel says
// this byte should be set to 0xFF
sendCommand(0xFF);
waitforIrq();
for(uint i = 0; i < 512; i++)
buffer[i] = Managarm.ManaIo.readMemDirect8(0xFF803000 + i);
// read the results (and ignore most bytes)
ubyte st0 = readResult();
readResult(); // st1 register
readResult(); // st2 register
readResult(); // current cylinder
readResult(); // current head
readResult(); // current sector
readResult(); // sector size constant
if(st0 & ST0_INT_CODE != 0)
System.errOut.writeLn("readWriteSectors() failed!");
}
// must only be called by the processing thread
private boolean seek(ubyte cylinder) {
// send the seek command
resetIrq();
sendCommand(CMD_SEEK);
sendCommand(currentDrive);
sendCommand(cylinder);
waitforIrq();
// we have to send an sense interrupt status command to make
// sure that the seek command succeded
sendCommand(CMD_SENSE_INT);
ubyte res_st0 = readResult();
ubyte res_pcn = readResult();
if(res_st0 & ST0_SEEK_END == 0 || res_pcn != cylinder)
return false;
return true;
// wait until the head is at the correct position
System.waitMillis(15);
}
// must only be called by the processing thread
private void selectDrive(ubyte drivenum) {
currentDrive = drivenum;
drives[cast[uint]drivenum].updateMotor();
if(drives[cast[uint]drivenum].motorState) {
updateDrive(); // update the fdc registers
}else{
// we have to start the motor and wait until it is running
drives[cast[uint]drivenum].motorState = true;
updateDrive(); // update the fdc registers
// wait until the motor is running
// intels says the motor should be running in 300ms for 3 1/2" disks
System.waitMillis(300);
}
}
// enable/disable motors and select the drive
// must only be called by the processing thread
private void updateDrive() {
// set the current drive flag and other basic flags
ubyte dorflags = currentDrive | DOR_NORESET | DOR_DMAGATE;
if(drives[0].motorState) dorflags |= DOR_MOTOR0;
if(drives[1].motorState) dorflags |= DOR_MOTOR1;
if(drives[2].motorState) dorflags |= DOR_MOTOR2;
if(drives[3].motorState) dorflags |= DOR_MOTOR3;
// write the dor register
Managarm.ManaIo.writeIo8(controllerIo, REG_DOR, dorflags);
}
// must only be called by the processing thread
private void resetController() {
// enter reset state and leave it immediately
resetIrq();
Managarm.ManaIo.writeIo8(controllerIo, REG_DOR, 0);
System.waitMicros(250); // XXX: make sure that 250 usecs are enough
Managarm.ManaIo.writeIo8(controllerIo, REG_DOR,
DOR_NORESET | DOR_DMAGATE);
waitforIrq();
// program the data rate
Managarm.ManaIo.writeIo8(controllerIo, REG_CCR, CCR_RATE_500K);
// send four sense interrupt commands (required by the specification)
for(uint i = 0; i < 4; i++) {
sendCommand(CMD_SENSE_INT);
ubyte res_st0 = readResult();
ubyte res_pcn = readResult();
}
// send the specify cmd
sendCommand(CMD_SPECIFY);
// set some magic numbers: activate non-dma mode,
// step rate: 3ms, head unload time: 240ms, head load time: 16ms
sendCommand(223);
sendCommand(2);
}
// sends a recalibrate command to the fdc
// returns true if the command was successful
// must only be called by the processing thread
private boolean recalibrate() {
// send the recalibrate command
resetIrq();
sendCommand(CMD_RECALIBRATE);
sendCommand(currentDrive);
waitforIrq();
// we have to send an sense interrupt status command to make
// sure that the recalibrate command succeded
sendCommand(CMD_SENSE_INT);
ubyte res_st0 = readResult();
ubyte res_pcn = readResult();
if(res_st0 & ST0_SEEK_END == 0 || res_pcn != 0)
return false;
return true;
}
// sends a command byte to the floppy disk controller
// must only be called by the processing thread
private void sendCommand(ubyte command) {
// wait until the fdc is ready
while(Managarm.ManaIo.readIo8(controllerIo, REG_MSR) & MSR_RQM == 0) {
// TODO: do something useful
}
Managarm.ManaIo.writeIo8(controllerIo, REG_DATA, command);
}
// receives a result byte from the floppy disk controller
// must only be called by the processing thread
private ubyte readResult() {
// wait until the fdc is ready
while(Managarm.ManaIo.readIo8(controllerIo, REG_MSR) & MSR_RQM == 0) {
// TODO: do something useful
}
return Managarm.ManaIo.readIo8(controllerIo, REG_DATA);
}
private void resetIrq() {
IntfThreads.block(IntfThreads.getCurrent(), false);
Managarm.ManaEvents.waitfor(controllerIrq);
}
private void waitforIrq() {
IntfThreads.sleep(IntfThreads.getCurrent(), false);
}
// FIXME: horrible hack
private void setupDma() {
uint address = 0x80000;
uint dmabytes = 511; // bytes to transfer minus one
// disable channel, flip-flop and mode
Managarm.ManaIo.writeIoDirect8(0x0A, 2 | 0x4);
Managarm.ManaIo.writeIoDirect8(0x0C, 0);
Managarm.ManaIo.writeIoDirect8(0x0B, 0x44 | 2);
// address
Managarm.ManaIo.writeIoDirect8(0x04, cast[ubyte]address & 0xFF);
Managarm.ManaIo.writeIoDirect8(0x04, cast[ubyte](address >> 8) & 0xFF);
Managarm.ManaIo.writeIoDirect8(0x81, cast[ubyte](address >> 16) & 0xFF);
// flip-flop
Managarm.ManaIo.writeIoDirect8(0x0C, 0);
// byte count
Managarm.ManaIo.writeIoDirect8(0x05, cast[ubyte]dmabytes & 0xFF);
Managarm.ManaIo.writeIoDirect8(0x05, cast[ubyte](dmabytes >> 8) & 0xFF);
// enable channel
Managarm.ManaIo.writeIoDirect8(0x0A, 2);
}
}
/* This file is part of the managarm operating system.
* Copyright (C) 2007, 2008, 2009 Alexander van der Grinten
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
// this file implements a map based on red-black trees
namespace Korona;
generic class MapIterator[KEYTYPE, OBJTYPE] extends $Iterator[$MapEntry[KEYTYPE, OBJTYPE]] {
// next element that will be returned
private $MapEntry[KEYTYPE, OBJTYPE] current;
// last element that was returned
private $MapEntry[KEYTYPE, OBJTYPE] lastElement;
constructor($MapEntry[KEYTYPE, OBJTYPE] root) {
if(root == null)
return;
// get the leftmost element
$MapEntry[KEYTYPE, OBJTYPE] element = root;
while(element.left != null)
element = element.left;
current = element;
}
// inherited from $Iterator
public boolean hasNext() {
if(current == null)
return false;
return true;
}
// inherited from $Iterator
public $MapEntry[KEYTYPE, OBJTYPE] next() {
// memorize the current element to return it later
lastElement = current;
// find the the next element in the tree
if(current.right != null) {
// it's in the node's right subtree
current = current.right;
while(current.left != null)
current = current.left;
return lastElement;
}
// it's at an upper layer of the tree
while(current.parent != null
&& current != current.parent.left)
current = current.parent;
if(current != null)
current = current.parent;
return lastElement;
}
public void remove() {
lastElement.remove();
__gc_destruct$(lastElement$);
}
}
generic class KeyIterator[KEYTYPE, OBJTYPE] extends $Iterator[KEYTYPE] {
// an iterator we get our objects from
private $Iterator[$MapEntry[KEYTYPE, OBJTYPE]] iterator;
public constructor($MapEntry[KEYTYPE, OBJTYPE] root) {
iterator = new $MapIterator[KEYTYPE, OBJTYPE](root);
}
// inherited from $Iterator
public boolean hasNext() {
return iterator.hasNext();
}
// inherited from $Iterator
public KEYTYPE next() {
return iterator.next().key;
}
// inherited from $Iterator
public void remove() {
iterator.remove();
}
$ifdef LIBKOR_MARGINAL_RUNTIME $<--
// inherited from Korona.Object
public void __destruct() {
__gc_destruct$(iterator$);
}
$-->
}
generic class ValueIterator[KEYTYPE, OBJTYPE] extends $Iterator[OBJTYPE] {
// an iterator we get our objects from
private $Iterator[$MapEntry[KEYTYPE, OBJTYPE]] iterator;
public constructor($MapEntry[KEYTYPE, OBJTYPE] root) {
iterator = new $MapIterator[KEYTYPE, OBJTYPE](root);
}
// inherited from $Iterator
public boolean hasNext() {
return iterator.hasNext();
}
// inherited from $Iterator
public OBJTYPE next() {
return iterator.next().object;
}
// inherited from $Iterator
public void remove() {
iterator.remove();
}
$ifdef LIBKOR_MARGINAL_RUNTIME $<--
// inherited from Korona.Object
public void __destruct() {
__gc_destruct$(iterator$);
}
$-->
}
generic class Map[KEYTYPE, OBJTYPE] {
private static final uint COLOR_RED = 1;
private static final uint COLOR_BLACK = 2;
// comparator used to compare different entries of this map
private $Comparator[local KEYTYPE] comparator;
// list of entries in this map
private $MapEntry[KEYTYPE, OBJTYPE] root;
private uint numberOfElements;
public final constructor($Comparator[local KEYTYPE] comparator) {
this.comparator = comparator;
}
// adds one entry to this map
public final void add(KEYTYPE key, OBJTYPE object) {
// create a new map entry
$MapEntry[KEYTYPE, OBJTYPE] element
= new $MapEntry[KEYTYPE, OBJTYPE](this, key, object);
numberOfElements++;
// if the tree is empty "element" becomes the root
if(root == null) {
root = element;
restoreAfterInsert(element);
return;
}
// add "element" as a leaf to the tree
$MapEntry[KEYTYPE, OBJTYPE] entry = root;
while(true) {
uint result = comparator.compare(key, entry.key);
/* ---- add it the the left subtree ---- */
if(result == Util.CMP_LESS) {
if(entry.left == null) {
entry.left = element;
element.parent = entry;
restoreAfterInsert(element);
return;
}else{
entry = entry.left;
}
/* ---- add it to the right subtree ---- */
}else if(result == Util.CMP_GREATER) {
if(entry.right == null) {
entry.right = element;
element.parent = entry;
restoreAfterInsert(element);
return;
}else{
entry = entry.right;
}
}else if(result == Util.CMP_EQUAL) {
System.errOut.put(Reflection.getClassName(this)).putln(":");
System.errOut.formatLn("add(): Element already in map sorted by %s",
arrayof(new String(Reflection.getClassName(comparator))));
System.exit();
}else{
System.errOut.formatLn("add(): No total order for %s",
arrayof(new String(Reflection.getClassName(comparator))));
System.exit();
}
}
}
public final void restoreAfterInsert($MapEntry[KEYTYPE, OBJTYPE] node) {
// color the node red
node.color = COLOR_RED;
// if "node" is the root of the tree, color it black
if(node.parent == null) {
node.color = COLOR_BLACK;
}else if(node.parent.color == COLOR_BLACK) {
// replacing a black "null" node by a red node
// with black leaves doesn't violate
// any red-black properties if the parent node is black.
// when a leaf is replaced by a red node and the parent is
// red property 4 is violated.
return;
}else{
$MapEntry[KEYTYPE, OBJTYPE] grandparent = node.getGrandparent();
$MapEntry[KEYTYPE, OBJTYPE] uncle = node.getUncle();
if(uncle != null && uncle.color == COLOR_RED) {
node.parent.color = COLOR_BLACK;
uncle.color = COLOR_BLACK;
grandparent.color = COLOR_RED;
restoreAfterInsert(grandparent);
}else if(node == node.parent.right
&& node.parent == grandparent.left) {
node.parent.rotateLeft();
furtherRestoreAfterInsert(node.left);
}else if(node == node.parent.left
&& node.parent == grandparent.right) {
node.parent.rotateRight();
furtherRestoreAfterInsert(node.right);
}else{
furtherRestoreAfterInsert(node);
}
}
}
public final void furtherRestoreAfterInsert($MapEntry[KEYTYPE, OBJTYPE] node) {
$MapEntry[KEYTYPE, OBJTYPE] grandparent = node.getGrandparent();
node.parent.color = COLOR_BLACK;
grandparent.color = COLOR_RED;
if(node == node.parent.left && node.parent == grandparent.left) {
grandparent.rotateRight();
}else{
grandparent.rotateLeft();
}
}
public void removeEntry($MapEntry[KEYTYPE, OBJTYPE] entry) {
if(entry.left != null && entry.right != null) {
// replace the node with its in-order successor
// the successor has only one child so deleting it is easy
$MapEntry[KEYTYPE, OBJTYPE] successor = entry.right;
while(successor.left != null)
successor = successor.left;
entry.swapWith(successor);
}
numberOfElements--;
// we can be sure entry has at most one child now
$MapEntry[KEYTYPE, OBJTYPE] child = null;
if(entry.left != null)
child = entry.left;
if(entry.right != null)
child = entry.right;
// we will replace "entry" by "child" and thus remove child from the tree
// entry = the element that will be deleted
// child = the element that will remain in the tree
// if the node we want to remove (entry) is red everything is fine
if(entry.color == COLOR_RED) {
entry.replaceBy(child);
if(child != null)
child.parent = entry.parent;
return;
}
// now we can assume that "entry" is black
// if the node that will take "entry"'s position is red
// we color it black and we are done
if(child != null && child.color == COLOR_RED) {
child.color = COLOR_BLACK;
entry.replaceBy(child);
if(child != null)
child.parent = entry.parent;
return;
}
// now we can assume that both entry and child are black nodes
// reverse the role of entry and child here
// we cannot replace them yet because if we did the rebalancing code
// won't work if "child" is null.
// entry = the element that will remain in the tree
// entry = the element that will be deleted
fixAfterRemove(entry);
// restore the original roles of "entry" and "child"
// and finally replace "entry" by "child"
entry.replaceBy(child);
if(child != null) {
child.parent = entry.parent;
// set the color of "child" to the new color of "entry"
child.color = entry.color;
}
}
public void fixAfterRemove($MapEntry[KEYTYPE, OBJTYPE] node) {
// note that "node" is always black.
// if we are deleting the root, everything is fine
if(node.parent == null)
return;
$MapEntry[KEYTYPE, OBJTYPE] sibling = node.getSibling();
if(sibling.color == COLOR_RED) {
node.parent.color = COLOR_RED;
sibling.color = COLOR_BLACK;
if(node == node.parent.left) {
node.parent.rotateLeft();
}else{
node.parent.rotateRight();
}
// note that rotations change the sibling of "node"
sibling = node.getSibling();
}
if(node.parent.color == COLOR_BLACK
&& sibling.color == COLOR_BLACK
&& (sibling.left == null || sibling.left.color == COLOR_BLACK)
&& (sibling.right == null || sibling.right.color == COLOR_BLACK)) {
sibling.color = COLOR_RED;
fixAfterRemove(node.parent);
}else if(node.parent.color == COLOR_RED
&& sibling.color == COLOR_BLACK
&& (sibling.left == null || sibling.left.color == COLOR_BLACK)
&& (sibling.right == null || sibling.right.color == COLOR_BLACK)) {
sibling.color = COLOR_RED;
node.parent.color = COLOR_BLACK;
}else if(node == node.parent.left
&& sibling.color == COLOR_BLACK
&& sibling.left != null && sibling.left.color == COLOR_RED
&& (sibling.right == null || sibling.right.color == COLOR_BLACK)) {
sibling.color = COLOR_RED;
sibling.left.color = COLOR_BLACK;
sibling.rotateRight();
furtherFixAfterRemove(node);
}else if(node == node.parent.right
&& sibling.color == COLOR_BLACK
&& (sibling.left == null || sibling.left.color == COLOR_BLACK)
&& sibling.right != null && sibling.right.color == COLOR_RED) {
sibling.color = COLOR_RED;
sibling.right.color = COLOR_BLACK;
sibling.rotateLeft();
furtherFixAfterRemove(node);
}else{
furtherFixAfterRemove(node);
}
}
public void furtherFixAfterRemove($MapEntry[KEYTYPE, OBJTYPE] node) {
$MapEntry[KEYTYPE, OBJTYPE] sibling = node.getSibling();
sibling.color = node.parent.color;
node.parent.color = COLOR_BLACK;
if(node == node.parent.left) {
sibling.right.color = COLOR_BLACK;
node.parent.rotateLeft();
}else{
sibling.left.color = COLOR_BLACK;
node.parent.rotateRight();
}
}
public final void assign(KEYTYPE key, OBJTYPE object) {
$MapEntry[KEYTYPE, OBJTYPE] entry = getEntry(key);
if(entry == null) {
// add a new entry to this map
add(key, object);
}else{
// change the object field of the entry
entry.object = object;
}
}
public final uint getSize() {
return numberOfElements;
}
public final boolean contains(local KEYTYPE key) {
if(getEntry(key) == null)
return false;
return true;
}
// returns the object associated to key
public final OBJTYPE get(local KEYTYPE key) {
$MapEntry[KEYTYPE, OBJTYPE] entry = getEntry(key);
// if there is no such entry, return null
if(entry == null)
return null;
// return the entry's object
return entry.object;
}
public final $MapEntry[KEYTYPE, OBJTYPE] getEntry(local KEYTYPE key) {
$MapEntry[KEYTYPE, OBJTYPE] entry = root;
while(entry != null) {
uint result = comparator.compare(key, entry.key);
/* ---- look it up in the the left subtree ---- */
if(result == Util.CMP_LESS) {
entry = entry.left;
/* ---- look it up in the right subtree ---- */
}else if(result == Util.CMP_GREATER) {
entry = entry.right;
}else if(result == Util.CMP_EQUAL) {
return entry;
}else{
System.errOut.formatLn("getEntry(): No total order for %s",
arrayof(new String(Reflection.getClassName(comparator))));
System.exit();
}
}
// we could not find the element
return null;
}
public final boolean empty() {
if(numberOfElements > 0)
return false;
return true;
}
public final void remove(KEYTYPE key) {
$MapEntry[KEYTYPE, OBJTYPE] entry = getEntry(key);
removeEntry(entry);
__gc_destruct$(entry$);
}
// removes a random element from the tree
// (in this implementation the root is removed.)
public final $MapEntry[KEYTYPE, OBJTYPE] remove() {
$MapEntry[KEYTYPE, OBJTYPE] entry = root;
removeEntry(entry);
return entry;
}
public final void clear() {
while(numberOfElements > 0) {
$MapEntry[KEYTYPE, OBJTYPE] entry = remove();
__gc_destruct$(entry$);
}
}
public final $Iterator[$MapEntry[KEYTYPE, OBJTYPE]] iterator() {
return new $MapIterator[KEYTYPE, OBJTYPE](root);
}
public final $Iterator[KEYTYPE] keyIterator() {
return new $KeyIterator[KEYTYPE, OBJTYPE](root);
}
public final $Iterator[OBJTYPE] valueIterator() {
return new $ValueIterator[KEYTYPE, OBJTYPE](root);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment