///|
fn Machine::object_addr(self : Machine, obj : Int32) -> Int32 raise Fault {
let limit = if self.version == 3 { 255 } else { 65535 }
if obj <= 0 || obj > limit {
raise Invalid("invalid object number")
}
let addr = self.objects +
(if self.version == 3 { 62 + (obj - 1) * 9 } else { 126 + (obj - 1) * 14 })
if addr > self.static_base - (if self.version == 3 { 9 } else { 14 }) {
raise Invalid("object outside dynamic memory")
}
addr
}
///|
fn Machine::relation(
self : Machine,
obj : Int32,
which : Int32,
) -> Int32 raise Fault {
if obj == 0 {
return 0
}
let p = self.object_addr(obj)
if self.version == 3 {
self.byte(p + 4 + which)
} else {
self.word(p + 6 + which * 2)
}
}
///|
fn Machine::set_relation(
self : Machine,
obj : Int32,
which : Int32,
value : Int32,
) -> Unit raise Fault {
let p = self.object_addr(obj)
if self.version == 3 {
self.put_byte(p + 4 + which, value)
} else {
self.put_word(p + 6 + which * 2, value)
}
}
///|
fn Machine::remove_object(self : Machine, obj : Int32) -> Unit raise Fault {
let parent = self.relation(obj, 0)
if parent == 0 {
return
}
let next = self.relation(obj, 1)
let first = self.relation(parent, 2)
if first == obj {
self.set_relation(parent, 2, next)
} else {
let mut cursor = first
let mut limit = 65536
while cursor != 0 && self.relation(cursor, 1) != obj {
cursor = self.relation(cursor, 1)
limit -= 1
if limit == 0 {
raise Invalid("cyclic sibling chain")
}
}
if cursor == 0 {
raise Invalid("object absent from parent")
}
self.set_relation(cursor, 1, next)
}
self.set_relation(obj, 0, 0)
self.set_relation(obj, 1, 0)
}
///|
fn Machine::insert_object(
self : Machine,
obj : Int32,
parent : Int32,
) -> Unit raise Fault {
let _ = self.object_addr(parent)
let mut p = parent
let mut limit = 65536
while p != 0 {
if p == obj {
raise Invalid("cyclic object insertion")
}
p = self.relation(p, 0)
limit -= 1
if limit == 0 {
raise Invalid("cyclic object tree")
}
}
self.remove_object(obj)
let child = self.relation(parent, 2)
self.set_relation(obj, 0, parent)
self.set_relation(obj, 1, child)
self.set_relation(parent, 2, obj)
}
///|
fn Machine::attribute(
self : Machine,
obj : Int32,
attr : Int32,
action : Int32,
) -> Bool raise Fault {
let max = if self.version == 3 { 32 } else { 48 }
if attr < 0 || attr >= max {
raise Invalid("invalid attribute number")
}
let addr = self.object_addr(obj) + attr / 8
let mask = 128 >> (attr % 8)
let b = self.byte(addr)
if action == 1 {
self.put_byte(addr, b | mask)
}
if action == 2 {
self.put_byte(addr, b & (255 ^ mask))
}
(b & mask) != 0
}
///|
fn Machine::property_table(self : Machine, obj : Int32) -> Int32 raise Fault {
self.word(self.object_addr(obj) + (if self.version == 3 { 7 } else { 12 }))
}
///|
fn Machine::property_info(
self : Machine,
addr : Int32,
) -> (Int32, Int32, Int32) raise Fault {
let b = self.byte(addr)
if b == 0 {
return (0, addr + 1, 0)
}
if self.version == 3 {
(b & 31, addr + 1, (b >> 5) + 1)
} else if (b & 128) == 0 {
(b & 63, addr + 1, if (b & 64) == 0 { 1 } else { 2 })
} else {
let size = self.byte(addr + 1) & 63
(b & 63, addr + 2, if size == 0 { 64 } else { size })
}
}
///|
fn Machine::find_property(
self : Machine,
obj : Int32,
prop : Int32,
) -> (Int32, Int32) raise Fault {
let table = self.property_table(obj)
let mut addr = table + 1 + 2 * self.byte(table)
let mut previous = if self.version == 3 { 32 } else { 64 }
while true {
let (number, data, size) = self.property_info(addr)
if number == 0 {
return (0, 0)
}
if number >= previous {
raise Invalid("properties are not in descending order")
}
if number == prop {
return (data, size)
}
previous = number
addr = data + size
} nobreak {
(0, 0)
}
}
///|
fn Machine::get_property(
self : Machine,
obj : Int32,
prop : Int32,
) -> Int32 raise Fault {
let max = if self.version == 3 { 31 } else { 63 }
if prop < 1 || prop > max {
raise Invalid("invalid property number")
}
let (addr, size) = self.find_property(obj, prop)
if addr == 0 {
self.word(self.objects + 2 * (prop - 1))
} else if size == 1 {
self.byte(addr)
} else {
self.word(addr)
}
}
///|
fn Machine::next_property(
self : Machine,
obj : Int32,
prop : Int32,
) -> Int32 raise Fault {
let table = self.property_table(obj)
let addr = if prop == 0 {
table + 1 + 2 * self.byte(table)
} else {
let (data, size) = self.find_property(obj, prop)
if data == 0 {
raise Invalid("property not found")
}
data + size
}
let (number, _, _) = self.property_info(addr)
number
}
///|
fn Machine::property_length(self : Machine, data : Int32) -> Int32 raise Fault {
if data == 0 {
return 0
}
let b = self.byte(data - 1)
if self.version == 3 {
(b >> 5) + 1
} else if (b & 128) != 0 {
let n = b & 63
if n == 0 {
64
} else {
n
}
} else if (b & 64) != 0 {
2
} else {
1
}
}