Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: memory management #6

Merged
merged 4 commits into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/memory/memory.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { test, expect, describe } from 'bun:test';
import { Memory, UnknownAddressError, WriteOnceError } from './memory';
import { Relocatable, RelocatableError } from './primitives/relocatable';
import { Felt } from './primitives/felt';
import { UnsignedInteger } from './primitives/uint';

describe('Memory', () => {
describe('get', () => {
test('should throw if address is not written to', () => {
const memory = new Memory();
const address = new Relocatable(0, 0);

expect(() => memory.get(address)).toThrow(new UnknownAddressError());
});
test('should return the value at the address', () => {
const memory = new Memory();
memory.numSegments = UnsignedInteger.toUint(1);
const address = new Relocatable(0, 0);
const value = new Felt(10n);
memory.insert(address, value);

expect(memory.get(address)).toEqual(value);
});
});
describe('insert', () => {
test('should throw if relocatable is out of memory segment bounds', () => {
const memory = new Memory();
const address = new Relocatable(1, 0);
const value = new Felt(10n);

expect(() => memory.insert(address, value)).toThrow(
new RelocatableError()
);
});
test('should throw if address is already written to', () => {
let memory = new Memory();
memory.numSegments = UnsignedInteger.toUint(1);
const address = new Relocatable(0, 0);
const value = new Felt(10n);
memory.insert(address, value);

expect(() => memory.insert(address, value)).toThrow(new WriteOnceError());
});
});
});
35 changes: 17 additions & 18 deletions src/memory/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export class MemoryError extends Error {}
export class WriteOnceError extends MemoryError {}
export class UnknownAddressError extends MemoryError {}

class Memory {
export class Memory {
data: Map<Relocatable, MaybeRelocatable>;
numSegments: Uint;

constructor(data: Map<Relocatable, MaybeRelocatable>, numSegments: number) {
this.data = data;
this.numSegments = UnsignedInteger.toUint(numSegments);
constructor() {
this.data = new Map();
this.numSegments = UnsignedInteger.toUint(0);
}

insert(address: Relocatable, value: MaybeRelocatable) {
Expand All @@ -30,37 +30,36 @@ class Memory {
this.data.set(address, value);
}

get(address: Relocatable) {
get(address: Relocatable): MaybeRelocatable {
const value = this.data.get(address);
if (value === undefined) {
throw new UnknownAddressError();
}
return value;
}
}

class MemorySegmentManager {
segmentSizes: Record<number, number>;
export class MemorySegmentManager {
segmentSizes: Record<Uint, Uint>;
Copy link
Member

@Eikix Eikix Sep 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general:

If you have a static set of keys or you want a type that behaves like a regular object but with type safety, Record is a good choice.
If you want a dynamic collection of key-value pairs with more utility methods and insertion order, then Map is the way to go.
Lastly, considering the branding on Uint, if you were to use the Record, keep in mind that object keys in JavaScript are always strings, so there might be some nuances with key conversion if you try to use the branded number directly as a key.

memory: Memory;

constructor(segmentSizes: Record<number, number>, memory: Memory) {
this.segmentSizes = segmentSizes;
this.memory = memory;
constructor() {
this.segmentSizes = {};
this.memory = new Memory();
Eikix marked this conversation as resolved.
Show resolved Hide resolved
}

addSegment() {
addSegment(): Relocatable {
const ptr = new Relocatable(this.memory.numSegments, 0);
this.memory.numSegments = UnsignedInteger.toUint(
this.memory.numSegments + 1
);
return new Relocatable(this.memory.numSegments - 1, 0);
return ptr;
}

loadData(address: Relocatable, data: MaybeRelocatable[]) {
loadData(address: Relocatable, data: MaybeRelocatable[]): Relocatable {
data.forEach((d, index) =>
this.memory.insert(
address.addPositiveNumber(UnsignedInteger.toUint(index)),
d
)
this.memory.insert(address.add(UnsignedInteger.toUint(index)), d)
Eikix marked this conversation as resolved.
Show resolved Hide resolved
);
return address.addPositiveNumber(UnsignedInteger.toUint(data.length));
return address.add(UnsignedInteger.toUint(data.length));
}
}
4 changes: 2 additions & 2 deletions src/memory/primitives/felt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { ConversionError, Felt, FeltError } from './felt';

describe('Felt', () => {
describe('constructor', () => {
test('should throw if initialising a felt with a negative inner', () => {
test('should throw if initializing a felt with a negative inner', () => {
Eikix marked this conversation as resolved.
Show resolved Hide resolved
expect(() => new Felt(-10n)).toThrow(new FeltError());
});
test('should throw a FeltError when initialising with a BigInt larger than PRIME', () => {
test('should throw a FeltError when initializing with a BigInt larger than PRIME', () => {
const biggerThanPrime = Felt.PRIME + 1n;
expect(() => new Felt(biggerThanPrime)).toThrow(new FeltError());
});
Expand Down
2 changes: 1 addition & 1 deletion src/memory/primitives/felt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class Felt {
}

toNumber(): number {
let num = Number(this.inner);
const num = Number(this.inner);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch

// The value of the largest integer n such that n and n + 1 are both exactly representable as a Number value.
// The value of Number.MAX_SAFE_INTEGER is 9007199254740991, i.e. 2^53 − 1.
if (num > Number.MAX_SAFE_INTEGER) {
Expand Down