Skip to content

Commit 35ab62f

Browse files
committed
refactor: use named constants for dict and segment arena related offset in hints
1 parent 80e05ca commit 35ab62f

11 files changed

+114
-53
lines changed

src/builtins/segmentArena.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,32 @@
11
import { BuiltinHandler } from './builtin';
22

3+
/**
4+
* Offset to compute the address
5+
* of the info segment pointer
6+
* in the segment arena builtin.
7+
*/
8+
export const INFO_PTR_OFFSET = 3;
9+
10+
/**
11+
* Offset to read the current number
12+
* of allocated dictionaries in the
13+
* segment arena builtin.
14+
*/
15+
export const DICT_NUMBER_OFFSET = 2;
16+
17+
/**
18+
* The segment arena builtin manages Cairo dictionaries.
19+
*
20+
* It works by block of 3 cells:
21+
* - The first cell contains the base address of the info pointer.
22+
* - The second cell contains the current number of allocated dictionaries.
23+
* - The third cell contains the current number of squashed dictionaries.
24+
*
25+
* The Info segment is tightly closed to the segment arena builtin.
26+
*
27+
* It also works by block of 3 cells:
28+
* - The first cell is the base address of a dictionary
29+
* - The second cell is the end address of a dictionary when squashed.
30+
* - The third cell is the current number of squashed dictionaries (i.e. its squashing index).
31+
*/
332
export const segmentArenaHandler: BuiltinHandler = {};

src/errors/dictionary.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Relocatable } from 'primitives/relocatable';
2+
3+
class DictionaryError extends Error {}
4+
5+
/** Cannot find Dictionary at `address` */
6+
export class DictNotFound extends DictionaryError {
7+
constructor(address: Relocatable) {
8+
super(`Cannot found any Dictionary at address ${address.toString()}`);
9+
}
10+
}

src/errors/virtualMachine.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,3 @@ export class InvalidBufferResOp extends VirtualMachineError {
6565
super(`Cannot extract buffer from the given ResOperand: ${resOperand}`);
6666
}
6767
}
68-
69-
/** Cannot find Dictionary at `address` */
70-
export class DictNotFound extends VirtualMachineError {
71-
constructor(address: Relocatable) {
72-
super(`Cannot found any Dictionary at address ${address.toString()}`);
73-
}
74-
}

src/hints/dict/allocFelt252Dict.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { z } from 'zod';
33
import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives';
44

55
import { isFelt, isRelocatable } from 'primitives/segmentValue';
6-
import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine';
6+
import { VirtualMachine } from 'vm/virtualMachine';
77

88
import { HintName } from 'hints/hintName';
99
import { resOperand, ResOperand } from 'hints/hintParamsSchema';
10+
import { DICT_ACCESS_SIZE } from 'hints/dictionary';
11+
import { DICT_NUMBER_OFFSET, INFO_PTR_OFFSET } from 'builtins/segmentArena';
1012

1113
/** Zod object to parse AllocFelt252Dict hint */
1214
export const allocFelt252DictParser = z
@@ -42,10 +44,10 @@ export const allocFelt252Dict = (
4244
segmentArenaPtr: ResOperand
4345
) => {
4446
const arenaPtr = vm.getPointer(...vm.extractBuffer(segmentArenaPtr));
45-
const dictNumber = vm.memory.get(arenaPtr.sub(2));
47+
const dictNumber = vm.memory.get(arenaPtr.sub(DICT_NUMBER_OFFSET));
4648
if (!dictNumber || !isFelt(dictNumber)) throw new ExpectedFelt(dictNumber);
4749

48-
const infoPtr = vm.memory.get(arenaPtr.sub(3));
50+
const infoPtr = vm.memory.get(arenaPtr.sub(INFO_PTR_OFFSET));
4951
if (!infoPtr || !isRelocatable(infoPtr))
5052
throw new ExpectedRelocatable(infoPtr);
5153

src/hints/dict/felt252DictEntryInit.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { VirtualMachine } from 'vm/virtualMachine';
55

66
import { resOperand, ResOperand } from 'hints/hintParamsSchema';
77
import { HintName } from 'hints/hintName';
8+
import { PREV_VALUE_OFFSET } from 'hints/dictionary';
89

910
/** Zod object to parse Felt252DictEntryInit hint */
1011
export const felt252DictEntryInitParser = z
@@ -47,5 +48,5 @@ export const felt252DictEntryInit = (
4748
const dict = vm.getDict(address);
4849
const prevValue = dict.get(keyValue) || new Felt(0n);
4950
dict.set(keyValue, prevValue);
50-
vm.memory.assertEq(address.add(1), prevValue);
51+
vm.memory.assertEq(address.add(PREV_VALUE_OFFSET), prevValue);
5152
};

src/hints/dict/felt252DictEntryUpdate.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import { z } from 'zod';
33
import { ExpectedFelt } from 'errors/primitives';
44

55
import { isFelt } from 'primitives/segmentValue';
6-
import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine';
6+
import { VirtualMachine } from 'vm/virtualMachine';
77

88
import { HintName } from 'hints/hintName';
99
import { Deref, OpType, resOperand, ResOperand } from 'hints/hintParamsSchema';
10+
import { KEY_OFFSET } from 'hints/dictionary';
1011

1112
/** Zod object to parse Felt252DictEntryUpdate hint */
1213
export const felt252DictEntryUpdateParser = z
@@ -45,7 +46,7 @@ export const felt252DictEntryUpdate = (
4546
value: ResOperand
4647
) => {
4748
const address = vm.getPointer(...vm.extractBuffer(dictPtr));
48-
const key = vm.memory.get(address.sub(DICT_ACCESS_SIZE));
49+
const key = vm.memory.get(address.sub(KEY_OFFSET));
4950
if (!key || !isFelt(key)) throw new ExpectedFelt(key);
5051
const val =
5152
value.type === OpType.Deref

src/hints/dict/initSquashData.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { z } from 'zod';
22

33
import { ExpectedFelt } from 'errors/primitives';
4+
import { InvalidDictAccessesNumber } from 'errors/hints';
45

56
import { Felt } from 'primitives/felt';
67
import { isFelt } from 'primitives/segmentValue';
7-
import { DICT_ACCESS_SIZE, VirtualMachine } from 'vm/virtualMachine';
8+
import { VirtualMachine } from 'vm/virtualMachine';
89

910
import { HintName } from 'hints/hintName';
1011
import {
@@ -13,7 +14,7 @@ import {
1314
resOperand,
1415
ResOperand,
1516
} from 'hints/hintParamsSchema';
16-
import { InvalidDictAccessesNumber } from 'errors/hints';
17+
import { DICT_ACCESS_SIZE } from 'hints/dictionary';
1718

1819
/** Zod object to parse InitSquashData hint */
1920
export const initSquashDataParser = z

src/hints/dictionary.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, test, expect } from 'bun:test';
2+
3+
import { DictNotFound } from 'errors/dictionary';
4+
5+
import { Felt } from 'primitives/felt';
6+
import { Relocatable } from 'primitives/relocatable';
7+
import { VirtualMachine } from 'vm/virtualMachine';
8+
import { Dictionary } from './dictionary';
9+
10+
describe('Dictionary', () => {
11+
test('should properly initialize the dict manager', () => {
12+
const vm = new VirtualMachine();
13+
expect(vm.dictManager.size).toEqual(0);
14+
});
15+
16+
test('should properly create a new dictionary', () => {
17+
const vm = new VirtualMachine();
18+
const address = vm.newDict();
19+
expect(address).toEqual(new Relocatable(0, 0));
20+
expect(vm.getDict(address)).toEqual(new Dictionary(new Felt(0n)));
21+
});
22+
23+
test('should throw DictNotFound when accessing a non-existing dictionary', () => {
24+
const vm = new VirtualMachine();
25+
const address = new Relocatable(2, 3);
26+
expect(() => vm.getDict(address)).toThrow(new DictNotFound(address));
27+
});
28+
});

src/hints/dictionary.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Felt } from 'primitives/felt';
2+
import { SegmentValue } from 'primitives/segmentValue';
3+
4+
export const DICT_ACCESS_SIZE = 3;
5+
6+
/** Offset to read the key of the entry to update. */
7+
export const KEY_OFFSET = 3;
8+
9+
/**
10+
* Offset to read the previous value
11+
* of the entry to read or update.
12+
*/
13+
export const PREV_VALUE_OFFSET = 1;
14+
15+
/**
16+
* Helper class to implement Cairo dictionaries.
17+
*
18+
* The `id` attribute is needed to keep track
19+
* of the multiple dictionaries and their
20+
* corresponding segment in memory.
21+
*/
22+
export class Dictionary extends Map<string, SegmentValue> {
23+
constructor(public readonly id: Felt) {
24+
super();
25+
this.id = id;
26+
}
27+
}

src/vm/virtualMachine.test.ts

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { test, expect, describe, spyOn } from 'bun:test';
22

33
import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives';
4-
import {
5-
DictNotFound,
6-
InvalidBufferResOp,
7-
UnusedRes,
8-
} from 'errors/virtualMachine';
4+
import { InvalidBufferResOp, UnusedRes } from 'errors/virtualMachine';
95

106
import { Felt } from 'primitives/felt';
117
import { Relocatable } from 'primitives/relocatable';
@@ -19,7 +15,7 @@ import {
1915
FpUpdate,
2016
Op1Src,
2117
} from './instruction';
22-
import { Dictionary, VirtualMachine } from './virtualMachine';
18+
import { VirtualMachine } from './virtualMachine';
2319
import { CellRef, Operation, OpType, ResOperand } from 'hints/hintParamsSchema';
2420

2521
const instructions = {
@@ -741,24 +737,4 @@ describe('VirtualMachine', () => {
741737
);
742738
});
743739
});
744-
745-
describe('Dictionary', () => {
746-
test('should properly initialize the dict manager', () => {
747-
const vm = new VirtualMachine();
748-
expect(vm.dictManager.size).toEqual(0);
749-
});
750-
751-
test('should properly create a new dictionary', () => {
752-
const vm = new VirtualMachine();
753-
const address = vm.newDict();
754-
expect(address).toEqual(new Relocatable(0, 0));
755-
expect(vm.getDict(address)).toEqual(new Dictionary(new Felt(0n)));
756-
});
757-
758-
test('should throw DictNotFound when accessing a non-existing dictionary', () => {
759-
const vm = new VirtualMachine();
760-
const address = new Relocatable(2, 3);
761-
expect(() => vm.getDict(address)).toThrow(new DictNotFound(address));
762-
});
763-
});
764740
});

0 commit comments

Comments
 (0)