Skip to content

Commit

Permalink
fix(ecs): update VersionedIDGen, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Nov 11, 2019
1 parent 9a01b4e commit 118405d
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 18 deletions.
51 changes: 33 additions & 18 deletions packages/ecs/src/id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,23 @@ export class IDGen {
}
}

export class VersionedIdGen extends IDGen {
export class VersionedIDGen {
ids: number[];
nextID: number;
capacity: number;
mask: number;
shift: number;
freeID: number;

constructor(bits: number, cap = (1 << bits) >>> 0, next = 0) {
const maxCap = (1 << bits) >>> 0;
assert(cap <= maxCap, "capacity too large for given bit size");
super(cap, next);
this.ids = [];
this.nextID = next;
this.capacity = cap;
this.mask = maxCap - 1;
this.shift = bits;
this.freeID = -1;
}

id(id: number) {
Expand All @@ -61,28 +68,36 @@ export class VersionedIdGen extends IDGen {
}

next() {
if (this.ids.length) {
const id = this.ids.pop()!;
return (
((id & this.mask) | ((id & ~this.mask) + (1 << this.shift))) >>>
0
);
if (this.freeID !== -1) {
const id = this.freeID;
const rawID = this.id(id);
this.freeID = this.ids[rawID];
this.ids[rawID] = id;
return id;
} else {
assert(this.nextID < this.capacity, "max capacity reached");
const id = this.nextID++;
this.ids[id] = id;
return id;
}
assert(this.nextID < this.capacity, "max capacity reached");
return this.nextID++;
}

free(id: number) {
// FIXME use this.isValid()
if ((id & this.mask) < this.nextID) {
this.ids.push(id);
return true;
}
return false;
if (!this.isValid(id)) return false;
this.ids[this.id(id)] = this.freeID;
this.freeID = this.nextVersion(id);
return true;
}

isValid(id: number) {
// FIXME need to check/iterate ids manually & apply mask
return super.isValid(id & this.mask);
const rawID = this.id(id);
if (id < 0 || rawID >= this.nextID) return false;
return this.version(this.ids[rawID]) === this.version(id);
}

protected nextVersion(id: number) {
return (
((id & this.mask) | ((id & ~this.mask) + (1 << this.shift))) >>> 0
);
}
}
26 changes: 26 additions & 0 deletions packages/ecs/test/id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as assert from "assert";
import { VersionedIDGen } from "../src";

describe("VersionedIDGen", () => {
it("reuse", () => {
const g = new VersionedIDGen(8);
assert.equal(g.next(), 0);
assert.equal(g.next(), 1);
assert.equal(g.next(), 2);
assert(g.free(1));
assert(g.free(2));
assert.equal(g.next(), 0x102);
assert.equal(g.next(), 0x101);
assert.equal(g.next(), 3);
assert(g.free(0));
assert(!g.free(0));
assert.equal(g.next(), 0x100);
assert(g.free(0x100));
assert(g.free(3));
assert.equal((<any>g).freeID, 0x103);
assert(g.free(0x101));
assert(g.free(0x102));
assert.equal((<any>g).freeID, 0x202);
assert.deepEqual((<any>g).ids, [-1, 0x103, 0x201, 0x200]);
});
});

0 comments on commit 118405d

Please sign in to comment.