Skip to content

Commit

Permalink
implement HintProcessorData (keep-starknet-strange#341)
Browse files Browse the repository at this point in the history
* HintProcessorData

* follow comments
  • Loading branch information
jobez authored Jan 31, 2024
1 parent 6fe50af commit d0bffb7
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 10 deletions.
161 changes: 161 additions & 0 deletions src/hint_processor/hint_processor_def.zig
Original file line number Diff line number Diff line change
@@ -1,11 +1,51 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
const StringHashMap = std.StringHashMap;

const CairoVMError = @import("../vm/error.zig").CairoVMError;
const OffsetValue = @import("../vm/types/programjson.zig").OffsetValue;
const ApTracking = @import("../vm/types/programjson.zig").ApTracking;
const Reference = @import("../vm/types/programjson.zig").Reference;

const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const expectError = std.testing.expectError;
const expectEqualStrings = std.testing.expectEqualStrings;

/// Represents a 'compiled' hint.
///
/// This structure the return type for the `compileHint` method in the HintProcessor interface
pub const HintProcessorData = struct {
const Self = @This();

allocator: Allocator,
/// Code string that is mapped by the processor to a corresponding implementation
code: []const u8,
/// Default ApTracking is initialized with group = 0 and offset = 0
ap_tracking: ApTracking = ApTracking{},
/// Maps a normalized reference name to its hint reference information
ids_data: std.StringHashMap(HintReference),


/// Initializes a unit of hint processor data with specified code and constructed id data mappings.
///
/// # Params
/// - `allocator`: The allocator that shares scope with the operating HintProcessor.
/// - `code`: Code string that the HintProcessor dispatches to execution logic.
/// - `ids_data`: A mapping from a normalized reference name to its `HintReference`, see `getIdsData` in this file for logic.
pub fn initDefault(allocator: Allocator, code: []const u8, ids_data: StringHashMap(HintReference)) Self {
return .{
.allocator = allocator,
.code = code,
.ids_data = ids_data,
};
}

pub fn deinit(self: *Self) void {
self.ids_data.deinit();
}
};

/// Represents a hint reference structure used for hints in Zig.
///
Expand Down Expand Up @@ -53,6 +93,41 @@ pub const HintReference = struct {
}
};

/// Takes a mapping from reference name to reference id, normalizes the reference name, and maps the normalized reference name to its denoted HintReference from a reference array.
///
/// Arguments:
/// - `allocator`: The allocator that shares scope with the operating HintProcessor.
/// - `reference_ids`: A mapping from reference name to reference id
/// - `references`: An array of HintReferences, as indexed by reference id
///
/// # Returns
/// A mapping of normalized reference name to HintReference, if successful
///
/// Errors
/// - If there is no corresponding reference in `references` array
pub fn getIdsData(allocator: Allocator, reference_ids: StringHashMap(usize), references: []const HintReference) !StringHashMap(HintReference) {
var ids_data = StringHashMap(HintReference).init(allocator);
errdefer ids_data.deinit();

var ref_id_it = reference_ids.iterator();
while (ref_id_it.next()) |ref_id_entry| {
const path = ref_id_entry.key_ptr.*;
const ref_id = ref_id_entry.value_ptr.*;

if (ref_id >= references.len) return CairoVMError.Unexpected;

var name_iterator = std.mem.splitBackwardsSequence(u8, path, ".");

const name = name_iterator.next() orelse return CairoVMError.Unexpected;
const ref_hint = references[ref_id];

try ids_data.put(name, ref_hint);
}

return ids_data;
}


test "HintReference: init should return a proper HintReference instance" {
try expectEqual(
HintReference{
Expand All @@ -78,3 +153,89 @@ test "HintReference: initSimple should return a proper HintReference instance" {
HintReference.initSimple(10),
);
}

test "HintProcessorData: initDefault returns a proper HintProcessorData instance" {
// Given
const allocator = std.testing.allocator;

// when
var reference_ids = StringHashMap(usize).init(allocator);
defer reference_ids.deinit();

var references = ArrayList(HintReference).init(allocator);
defer references.deinit();

// Add reference data
try reference_ids.put("starkware.cairo.common.math.split_felt.high", 0);
try reference_ids.put("starkware.cairo.common.math.split_felt.low", 1);

// add hint reference structs
try references.append(HintReference.initSimple(10));
try references.append(HintReference.initSimple(20));

// then
const code: []const u8 = "memory[ap] = segments.add()";

const ids_data = try getIdsData(allocator, reference_ids, references.items);

var hp_data = HintProcessorData.initDefault(allocator, code, ids_data);
defer hp_data.deinit();

try expectEqual(@as(usize, 0), hp_data.ap_tracking.group);
try expectEqual(@as(usize, 0), hp_data.ap_tracking.offset);
try expectEqualStrings(code, hp_data.code);
}


test "getIdsData: should map (ref name x ref id) x (ref data) as (ref name x ref data)" {
// Given
const allocator = std.testing.allocator;

// when
var reference_ids = StringHashMap(usize).init(allocator);
defer reference_ids.deinit();

var references = ArrayList(HintReference).init(allocator);
defer references.deinit();

// Add reference data
try reference_ids.put("starkware.cairo.common.math.split_felt.high", 0);
try reference_ids.put("starkware.cairo.common.math.split_felt.low", 1);

// add hint reference structs
try references.append(HintReference.initSimple(10));
try references.append(HintReference.initSimple(20));

// then
var ids_data = try getIdsData(allocator, reference_ids, references.items);
defer ids_data.deinit();

try expectEqual(ids_data.get("high").?.offset1.reference, .{ .FP, 10, false });
try expectEqual(ids_data.get("low").?.offset1.reference, .{ .FP, 20, false });

}

test "getIdsData: should throw Unexpected when there is no ref data corresponding to ref ids mapping" {
// Given
const allocator = std.testing.allocator;

// when
var reference_ids = StringHashMap(usize).init(allocator);
defer reference_ids.deinit();

var references = ArrayList(HintReference).init(allocator);
defer references.deinit();

// Add reference data
try reference_ids.put("starkware.cairo.common.math.split_felt.high", 0);
try reference_ids.put("starkware.cairo.common.math.split_felt.low", 1);

// add hint reference structs
try references.append(HintReference.initSimple(10));

// then
try expectError(
CairoVMError.Unexpected,
getIdsData(allocator, reference_ids, references.items),
);
}
2 changes: 2 additions & 0 deletions src/vm/error.zig
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ pub const CairoVMError = error{
InvalidApUpdate,
/// Invalid Opcode
InvalidOpcode,
/// Unexpected Failure
Unexpected,
};

/// Represents different error conditions that are memory-related.
Expand Down
13 changes: 3 additions & 10 deletions src/vm/types/programjson.zig
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,10 @@ pub const OffsetValue = union(enum) {
pub const ApTracking = struct {
const Self = @This();
/// Indicates register state deducibility (increases by 1 after an unknown change).
group: usize,
group: usize = 0,
/// Reflects Ap register changes within the same `group`.
offset: usize,

/// Initializes a new `ApTracking` instance.
///
/// Returns:
/// A new `ApTracking` instance with `group` and `offset` set to 0.
pub fn init() Self {
return .{ .group = 0, .offset = 0 };
}
offset: usize = 0,

};

/// Represents tracking data for references considering various program flows.
Expand Down

0 comments on commit d0bffb7

Please sign in to comment.