Skip to content

Commit

Permalink
Properly implement object_bytes
Browse files Browse the repository at this point in the history
Necessary for quota support:

#7
  • Loading branch information
rdaum committed Jan 13, 2024
1 parent b8bfac7 commit df2b381
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 17 deletions.
3 changes: 3 additions & 0 deletions crates/db/src/db_tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ pub trait DbTransaction {
/// Get the contents of the given object.
async fn get_object_contents(&self, obj: Objid) -> Result<ObjSet, WorldStateError>;

/// Get the stored size of the given object & all its properties, verbs, etc.
async fn get_object_size_bytes(&self, obj: Objid) -> Result<usize, WorldStateError>;

/// Set the location of the given object.
async fn set_object_location(&self, obj: Objid, location: Objid)
-> Result<(), WorldStateError>;
Expand Down
5 changes: 5 additions & 0 deletions crates/db/src/db_worldstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ impl WorldState for DbTxWorldState {
self.tx.get_object_location(obj).await
}

async fn object_bytes(&self, perms: Objid, obj: Objid) -> Result<usize, WorldStateError> {
self.perms(perms).await?.check_wizard()?;
self.tx.get_object_size_bytes(obj).await
}

#[tracing::instrument(skip(self))]
async fn create_object(
&mut self,
Expand Down
40 changes: 40 additions & 0 deletions crates/db/src/odb/object_relations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,19 @@ pub async fn get_object_value<Codomain: Clone + Eq + PartialEq + AsByteBuffer>(
}
}

pub async fn tuple_size_for_object_domain(
tx: &Transaction,
rel: WorldStateRelation,
oid: Objid,
) -> Option<usize> {
let relation = tx.relation(RelationId(rel as usize)).await;
match relation.seek_by_domain(oid.as_sliceref()).await {
Ok(t) => Some(t.slot_buffer().len()),
Err(TupleError::NotFound) => None,
Err(e) => panic!("Unexpected error: {:?}", e),
}
}

pub async fn get_object_by_codomain<Codomain: Clone + Eq + PartialEq + AsByteBuffer>(
tx: &Transaction,
rel: WorldStateRelation,
Expand All @@ -160,6 +173,18 @@ pub async fn get_object_by_codomain<Codomain: Clone + Eq + PartialEq + AsByteBuf
ObjSet::from_oid_iter(objs)
}

pub async fn tuple_size_for_object_codomain(
tx: &Transaction,
rel: WorldStateRelation,
oid: Objid,
) -> Option<usize> {
let relation = tx.relation(RelationId(rel as usize)).await;
match relation.seek_by_codomain(oid.as_sliceref()).await {
Ok(ts) => Some(ts.iter().map(|t| t.slot_buffer().len()).sum()),
Err(TupleError::NotFound) => None,
Err(e) => panic!("Unexpected error: {:?}", e),
}
}
pub async fn get_composite_value<Codomain: Clone + Eq + PartialEq + AsByteBuffer>(
tx: &Transaction,
rel: WorldStateRelation,
Expand All @@ -175,6 +200,21 @@ pub async fn get_composite_value<Codomain: Clone + Eq + PartialEq + AsByteBuffer
}
}

pub async fn tuple_size_composite(
tx: &Transaction,
rel: WorldStateRelation,
oid: Objid,
uuid: Uuid,
) -> Option<usize> {
let key_bytes = composite_key_for(oid, &uuid);
let relation = tx.relation(RelationId(rel as usize)).await;
match relation.seek_by_domain(key_bytes).await {
Ok(t) => Some(t.slot_buffer().len()),
Err(TupleError::NotFound) => None,
Err(e) => panic!("Unexpected error: {:?}", e),
}
}

#[allow(dead_code)]
async fn insert_composite_value<Codomain: Clone + Eq + PartialEq + AsByteBuffer>(
tx: &Transaction,
Expand Down
107 changes: 107 additions & 0 deletions crates/db/src/odb/rb_worldstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,113 @@ impl DbTransaction for RelBoxTransaction {
Ok(ov.is_some())
}

async fn get_object_size_bytes(&self, obj: Objid) -> Result<usize, WorldStateError> {
let mut size = 0;
size += object_relations::tuple_size_for_object_domain(
&self.tx,
WorldStateRelation::ObjectFlags,
obj,
)
.await
.unwrap_or(0);

size += object_relations::tuple_size_for_object_domain(
&self.tx,
WorldStateRelation::ObjectName,
obj,
)
.await
.unwrap_or(0);

size += object_relations::tuple_size_for_object_domain(
&self.tx,
WorldStateRelation::ObjectOwner,
obj,
)
.await
.unwrap_or(0);

size += object_relations::tuple_size_for_object_domain(
&self.tx,
WorldStateRelation::ObjectParent,
obj,
)
.await
.unwrap_or(0);

size += object_relations::tuple_size_for_object_domain(
&self.tx,
WorldStateRelation::ObjectLocation,
obj,
)
.await
.unwrap_or(0);

if let Some(verbs) = object_relations::get_object_value::<VerbDefs>(
&self.tx,
WorldStateRelation::ObjectVerbs,
obj,
)
.await
{
size += object_relations::tuple_size_for_object_domain(
&self.tx,
WorldStateRelation::ObjectVerbs,
obj,
)
.await
.unwrap_or(0);

for v in verbs.iter() {
size += object_relations::tuple_size_composite(
&self.tx,
WorldStateRelation::VerbProgram,
obj,
v.uuid(),
)
.await
.unwrap_or(0)
}
};

if let Some(props) = object_relations::get_object_value::<PropDefs>(
&self.tx,
WorldStateRelation::ObjectPropDefs,
obj,
)
.await
{
size += object_relations::tuple_size_for_object_domain(
&self.tx,
WorldStateRelation::ObjectPropDefs,
obj,
)
.await
.unwrap_or(0);

for p in props.iter() {
size += object_relations::tuple_size_composite(
&self.tx,
WorldStateRelation::ObjectPropertyValue,
obj,
p.uuid(),
)
.await
.unwrap_or(0)
}
};

size += object_relations::tuple_size_for_object_codomain(
&self.tx,
WorldStateRelation::ObjectLocation,
obj,
)
.await
.unwrap_or(0);

Ok(size)
}

async fn commit(&self) -> Result<CommitResult, WorldStateError> {
match self.tx.commit().await {
Ok(_) => Ok(CommitResult::Success),
Expand Down
9 changes: 0 additions & 9 deletions crates/kernel/src/builtins/bf_objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,15 +347,6 @@ async fn bf_recycle<'a>(bf_args: &mut BfCallState<'a>) -> Result<BfRet, Error> {
}
bf_declare!(recycle, bf_recycle);

/*
Function: int object_bytes (obj object)
Returns the number of bytes of the server's memory required to store the given object, including the space used by the values of all of its non-clear properties and by the verbs and properties defined directly on the object. Raised E_INVARG if object is not a valid object and E_PERM if the programmer is not a wizard.
*/

/*
Function: obj max_object ()
Returns the largest object number yet assigned to a created object. Note that the object with this number may no longer exist; it may have been recycled. The next object created will be assigned the object number one larger than the value of max_object().
*/
async fn bf_max_object<'a>(bf_args: &mut BfCallState<'a>) -> Result<BfRet, Error> {
if !bf_args.args.is_empty() {
return Err(E_INVARG);
Expand Down
16 changes: 10 additions & 6 deletions crates/kernel/src/builtins/bf_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,20 @@ async fn bf_length<'a>(bf_args: &mut BfCallState<'a>) -> Result<BfRet, Error> {
bf_declare!(length, bf_length);

async fn bf_object_bytes<'a>(bf_args: &mut BfCallState<'a>) -> Result<BfRet, Error> {
// TODO: we fake this to make cores work. But in reality calculating the size of an object
// would be too expensive and awkward in general. We'd need to go looking for all its verbs
// and properties and add them up. So we just return 128 for now.
if bf_args.args.len() != 1 {
return Err(E_INVARG);
}
let Variant::Obj(_) = bf_args.args[0].variant() else {
return Err(E_TYPE);
let Variant::Obj(o) = bf_args.args[0].variant() else {
return Err(E_INVARG);
};
if !bf_args.world_state.valid(*o).await? {
return Err(E_INVARG);
};
Ok(Ret(v_int(128)))
let size = bf_args
.world_state
.object_bytes(bf_args.caller_perms(), *o)
.await?;
Ok(Ret(v_int(size as i64)))
}
bf_declare!(object_bytes, bf_object_bytes);

Expand Down
2 changes: 1 addition & 1 deletion crates/kernel/tests/textdump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ mod test {
let (db, _) = RelBoxWorldState::open(None, 1 << 30).await;
let db = Arc::new(db);
let tx = db.clone().loader_client().unwrap();
textdump_load(tx.clone(), PathBuf::from(minimal_db))
textdump_load(tx.clone(), minimal_db)
.await
.unwrap();
assert_eq!(tx.commit().await.unwrap(), CommitResult::Success);
Expand Down
3 changes: 3 additions & 0 deletions crates/values/src/model/world_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ pub trait WorldState: Send + Sync {
/// Get the location of the given object.
async fn location_of(&self, perms: Objid, obj: Objid) -> Result<Objid, WorldStateError>;

/// Return the number of bytes used by the given object and all its attributes.
async fn object_bytes(&self, perms: Objid, obj: Objid) -> Result<usize, WorldStateError>;

/// Create a new object, assigning it a new unique object id.
/// If owner is #-1, the object's is set to itself.
/// Note it is the caller's responsibility to execute :initialize).
Expand Down
2 changes: 1 addition & 1 deletion doc/bf_functions_status.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ The following is a table of the status of various builtin-functions, to keep an
| binary_hash | | |
| decode_binary | | Probably won't implement, see README.md |
| encode_binary | | " |
| object_bytes | &check; | Fake value. Only there to make JHCore happy. Actually calculating this would be tricky |
| object_bytes | &check; | |

### Server

Expand Down

0 comments on commit df2b381

Please sign in to comment.