diff --git a/src/cairoUtilFuncGen/storage/dynArrayIndexAccess.ts b/src/cairoUtilFuncGen/storage/dynArrayIndexAccess.ts index 69bc1141e..930ebbabb 100644 --- a/src/cairoUtilFuncGen/storage/dynArrayIndexAccess.ts +++ b/src/cairoUtilFuncGen/storage/dynArrayIndexAccess.ts @@ -1,4 +1,5 @@ import assert from 'assert'; +import endent from 'endent'; import { DataLocation, FunctionCall, @@ -10,7 +11,6 @@ import { import { AST } from '../../ast/ast'; import { CairoType, TypeConversionContext } from '../../utils/cairoTypeSystem'; import { createCairoGeneratedFunction, createCallToFunction } from '../../utils/functionGeneration'; -import { U128_FROM_FELT, UINT256_LT } from '../../utils/importPaths'; import { createUint256TypeName } from '../../utils/nodeTemplates'; import { isDynamicArray, safeGetNodeType } from '../../utils/nodeTypeProcessing'; import { typeNameFromTypeNode } from '../../utils/utils'; @@ -71,29 +71,22 @@ export class DynArrayIndexAccessGen extends StringIndexedFuncGen { const funcName = `${arrayName}_IDX`; return { name: funcName, - code: [ - `func ${funcName}{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr : felt}(ref: felt, index: Uint256) -> (res: felt){`, - ` alloc_locals;`, - ` let (length) = ${lengthName}.read(ref);`, - ` let (inRange) = uint256_lt(index, length);`, - ` assert inRange = 1;`, - ` let (existing) = ${arrayName}.read(ref, index);`, - ` if (existing == 0){`, - ` let (used) = WARP_USED_STORAGE.read();`, - ` WARP_USED_STORAGE.write(used + ${valueCairoType.width});`, - ` ${arrayName}.write(ref, index, used);`, - ` return (used,);`, - ` }else{`, - ` return (existing,);`, - ` }`, - `}`, - ].join('\n'), - functionsCalled: [ - this.requireImport(...U128_FROM_FELT), - this.requireImport(...UINT256_LT), - arrayDef, - arrayLength, - ], + code: endent` + fn ${funcName}(warp_storage_array_ptr: felt252, index: u256) -> felt252 { + let length = ${lengthName}::read(warp_storage_array_ptr); + assert(index < length, 'Index out of bounds'); + let existing = ${arrayName}::read(warp_storage_array_ptr, index); + if existing == 0 { + let used = WARP_USED_STORAGE::read(); + WARP_USED_STORAGE::write(used + ${valueCairoType.width}); + ${arrayName}::write((warp_storage_array_ptr, index), used); + used + } else { + existing + } + } + `, + functionsCalled: [arrayDef, arrayLength], }; } } diff --git a/src/cairoUtilFuncGen/storage/dynArrayPop.ts b/src/cairoUtilFuncGen/storage/dynArrayPop.ts index 1d105b871..2da73fb60 100644 --- a/src/cairoUtilFuncGen/storage/dynArrayPop.ts +++ b/src/cairoUtilFuncGen/storage/dynArrayPop.ts @@ -1,4 +1,5 @@ import assert from 'assert'; +import endent from 'endent'; import { ArrayType, BytesType, @@ -14,7 +15,7 @@ import { AST } from '../../ast/ast'; import { CairoFunctionDefinition } from '../../export'; import { CairoType, TypeConversionContext } from '../../utils/cairoTypeSystem'; import { createCairoGeneratedFunction, createCallToFunction } from '../../utils/functionGeneration'; -import { U128_FROM_FELT, UINT256_EQ, UINT256_SUB } from '../../utils/importPaths'; +import { U256_FROM_FELTS } from '../../utils/importPaths'; import { getElementType, isDynamicArray, @@ -88,31 +89,27 @@ export class DynArrayPopGen extends StringIndexedFuncGen { const getElemLoc = isDynamicArray(elementType) || isMapping(elementType) - ? [ - `let (elem_loc) = ${arrayName}.read(loc, newLen);`, - `let (elem_loc) = readId(elem_loc);`, - ].join('\n') - : `let (elem_loc) = ${arrayName}.read(loc, newLen);`; + ? endent` + let elem_loc_id = ${arrayName}::read((loc, newLen)); + let elem_loc = readId(elem_loc_id); + ` + : `let elem_loc = ${arrayName}::read((loc, newLen));`; const funcName = `${arrayName}_POP`; return { name: funcName, - code: [ - `func ${funcName}{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr : felt}(loc: felt) -> (){`, - ` alloc_locals;`, - ` let (len) = ${lengthName}.read(loc);`, - ` let (isEmpty) = uint256_eq(len, Uint256(0,0));`, - ` assert isEmpty = 0;`, - ` let (newLen) = uint256_sub(len, Uint256(1,0));`, - ` ${lengthName}.write(loc, newLen);`, - ` ${getElemLoc}`, - ` return ${deleteFunc.name}(elem_loc);`, - `}`, - ].join('\n'), + code: endent` + fn ${funcName}(loc: felt252) { + let len = ${lengthName}::read(loc); + assert(len > u256_from_felts(0,0), 'Pop of empty list'); + let newLen = len - u256_from_felts(1,0); + ${lengthName}::write(loc, newLen); + ${getElemLoc} + return ${deleteFunc.name}(elem_loc); + } + `, functionsCalled: [ - this.requireImport(...U128_FROM_FELT), - this.requireImport(...UINT256_EQ), - this.requireImport(...UINT256_SUB), + this.requireImport(...U256_FROM_FELTS), deleteFunc, dynArray, dynArrayLength, diff --git a/src/cairoUtilFuncGen/storage/dynArrayPushWithArg.ts b/src/cairoUtilFuncGen/storage/dynArrayPushWithArg.ts index f1b900d97..838031671 100644 --- a/src/cairoUtilFuncGen/storage/dynArrayPushWithArg.ts +++ b/src/cairoUtilFuncGen/storage/dynArrayPushWithArg.ts @@ -29,7 +29,7 @@ import { specializeType, } from '../../utils/nodeTypeProcessing'; import { ImplicitArrayConversion } from '../calldata/implicitArrayConversion'; -import { U128_FROM_FELT, UINT256_ADD } from '../../utils/importPaths'; +import { U256_FROM_FELTS } from '../../utils/importPaths'; import endent from 'endent'; export class DynArrayPushWithArgGen extends StringIndexedFuncGen { @@ -145,39 +145,34 @@ export class DynArrayPushWithArgGen extends StringIndexedFuncGen { const arrayName = dynArray.name; const lengthName = dynArrayLength.name; const funcName = `${arrayName}_PUSHV${this.generatedFunctionsDef.size}`; - - const implicit = argLoc === DataLocation.Memory ? '#[implicit(warp_memory)]' : ''; + const implicit = argLoc === DataLocation.Memory ? '#[implicit(warp_memory: WarpMemory)]' : ''; const callWriteFunc = (cairoVar: string) => isDynamicArray(argType) || argType instanceof MappingType - ? [`let (elem_id) = readId(${cairoVar});`, `${elementWriteDef.name}(elem_id, value);`] - : [`${elementWriteDef.name}(${cairoVar}, value);`]; + ? endent` + let elem_id = readId(${cairoVar}); + ${elementWriteDef.name}(elem_id, value);` + : `${elementWriteDef.name}(${cairoVar}, value);`; return { name: funcName, code: endent` ${implicit} - func ${funcName}(loc: felt, value: ${inputType}) -> (){ - alloc_locals; - let (len) = ${lengthName}.read(loc); - let (newLen, carry) = uint256_add(len, Uint256(1,0)); - assert carry = 0; - ${lengthName}.write(loc, newLen); - let (existing) = ${arrayName}.read(loc, len); - if (existing == 0){ - let (used) = WARP_USED_STORAGE.read(); - WARP_USED_STORAGE.write(used + ${allocationCairoType.width}); - ${arrayName}.write(loc, len, used); - ${callWriteFunc('used').join('\n')} - }else{ - ${callWriteFunc('existing').join('\n')} + fn ${funcName}(loc: felt252, value: ${inputType}) { + let len = ${lengthName}::read(loc); + ${lengthName}::write(loc, len + u256_from_felts(1,0)); + let existing = ${arrayName}::read((loc, len)); + if existing == 0 { + let used = WARP_USED_STORAGE::read(); + WARP_USED_STORAGE::write(used + ${allocationCairoType.width}); + ${arrayName}::write((loc, len), used); + ${callWriteFunc('used')} + } else { + ${callWriteFunc('existing')} } - return (); - } - `, + }`, functionsCalled: [ - this.requireImport(...U128_FROM_FELT), - this.requireImport(...UINT256_ADD), + this.requireImport(...U256_FROM_FELTS), elementWriteDef, dynArray, dynArrayLength, diff --git a/src/cairoUtilFuncGen/storage/dynArrayPushWithoutArg.ts b/src/cairoUtilFuncGen/storage/dynArrayPushWithoutArg.ts index 323e7ffd1..e2c2495ce 100644 --- a/src/cairoUtilFuncGen/storage/dynArrayPushWithoutArg.ts +++ b/src/cairoUtilFuncGen/storage/dynArrayPushWithoutArg.ts @@ -1,4 +1,5 @@ import assert from 'assert'; +import endent from 'endent'; import { ArrayType, BytesType, @@ -13,7 +14,7 @@ import { AST } from '../../ast/ast'; import { printTypeNode } from '../../export'; import { CairoType, TypeConversionContext } from '../../utils/cairoTypeSystem'; import { createCairoGeneratedFunction, createCallToFunction } from '../../utils/functionGeneration'; -import { U128_FROM_FELT, UINT256_ADD } from '../../utils/importPaths'; +import { U256_FROM_FELTS } from '../../utils/importPaths'; import { getElementType, safeGetNodeType } from '../../utils/nodeTypeProcessing'; import { typeNameFromTypeNode } from '../../utils/utils'; import { GeneratedFunctionInfo, StringIndexedFuncGen } from '../base'; @@ -71,30 +72,22 @@ export class DynArrayPushWithoutArgGen extends StringIndexedFuncGen { const funcName = `${arrayName}_PUSH`; return { name: funcName, - code: [ - `func ${funcName}{syscall_ptr : felt*, pedersen_ptr : HashBuiltin*, range_check_ptr : felt}(loc: felt) -> (newElemLoc: felt){`, - ` alloc_locals;`, - ` let (len) = ${lengthName}.read(loc);`, - ` let (newLen, carry) = uint256_add(len, Uint256(1,0));`, - ` assert carry = 0;`, - ` ${lengthName}.write(loc, newLen);`, - ` let (existing) = ${arrayName}.read(loc, len);`, - ` if ((existing) == 0){`, - ` let (used) = WARP_USED_STORAGE.read();`, - ` WARP_USED_STORAGE.write(used + ${cairoElementType.width});`, - ` ${arrayName}.write(loc, len, used);`, - ` return (used,);`, - ` }else{`, - ` return (existing,);`, - ` }`, - `}`, - ].join('\n'), - functionsCalled: [ - this.requireImport(...U128_FROM_FELT), - this.requireImport(...UINT256_ADD), - dynArray, - dynArrayLength, - ], + code: endent` + fn ${funcName}(loc: felt252) -> felt252 { + let len = ${lengthName}::read(loc); + ${lengthName}::write(loc, len + u256_from_felts(1,0)); + let existing = ${arrayName}::read((loc, len)); + if existing == 0 { + let used = WARP_USED_STORAGE::read(); + WARP_USED_STORAGE::write(used + ${cairoElementType.width}); + ${arrayName}::write((loc, len), used); + used; + } else { + existing; + } + } + `, + functionsCalled: [this.requireImport(...U256_FROM_FELTS), dynArray, dynArrayLength], }; } } diff --git a/src/cairoUtilFuncGen/storage/storageDelete.ts b/src/cairoUtilFuncGen/storage/storageDelete.ts index 05532ada3..40e1027f1 100644 --- a/src/cairoUtilFuncGen/storage/storageDelete.ts +++ b/src/cairoUtilFuncGen/storage/storageDelete.ts @@ -123,14 +123,15 @@ export class StorageDeleteGen extends StringIndexedFuncGen { this.creatingFunctions.set(generateKey(type), funcName); const cairoType = CairoType.fromSol(type, this.ast); + const setToZeroInstructions = mapRange( + cairoType.width, + (n) => `WARP_STORAGE::write(${add('loc', n)}, 0);`, + ).join('\n'); return { name: funcName, code: endent` - func ${funcName}(loc: felt){ - ${mapRange(cairoType.width, (n) => ` WARP_STORAGE.write(${add('loc', n)}, 0);`).join( - '\n', - )} - return (); + fn ${funcName}(loc: felt252){ + ${setToZeroInstructions} } `, functionsCalled: [], diff --git a/src/cairoWriter/writers/cairoContractWriter.ts b/src/cairoWriter/writers/cairoContractWriter.ts index 00d72d475..84875929c 100644 --- a/src/cairoWriter/writers/cairoContractWriter.ts +++ b/src/cairoWriter/writers/cairoContractWriter.ts @@ -85,7 +85,7 @@ export class CairoContractWriter extends CairoASTNodeWriter { WARP_STORAGE: LegacyMap::, WARP_USED_STORAGE: felt252, WARP_NAMEGEN: felt252, - ${otherStorageVars.map((v) => `${writer.write(v)}`).join('\n')} + ${otherStorageVars.map((v) => `${writer.write(v)},`).join('\n')} } fn readId(loc: felt252) -> felt252 { diff --git a/src/utils/importFuncGenerator.ts b/src/utils/importFuncGenerator.ts index e7a273eeb..115d4790f 100644 --- a/src/utils/importFuncGenerator.ts +++ b/src/utils/importFuncGenerator.ts @@ -137,6 +137,9 @@ export function createImport( Paths.ARRAY, Paths.ARRAY_TRAIT, Paths.WARP_MEMORY, + Paths.WM_NEW, + Paths.WM_DYN_ARRAY_LENGTH, + Paths.WM_INDEX_DYN, Paths.MEMORY_TRAIT, Paths.BOOL_INTO_FELT252, Paths.FELT252_INTO_BOOL, diff --git a/warplib/src/integer.cairo b/warplib/src/integer.cairo index 539745d31..3a3b3054e 100644 --- a/warplib/src/integer.cairo +++ b/warplib/src/integer.cairo @@ -1,4 +1,6 @@ use integer::u128_try_from_felt252; +use integer::u128_to_felt252; +use integer::u256_from_felt252; use serde::BoolSerde; use array::ArrayImpl; use option::OptionTrait; @@ -33,3 +35,16 @@ fn bool_into_felt252(val: bool) -> felt252 { let resp = OptionTraitImpl::::unwrap(resp_option); resp } + +fn u256_to_felt252_safe(val: u256) -> felt252 { + let low_felt252 = u128_to_felt252(val.low); + let high_felt252 = u128_to_felt252(val.high); + + let two_pow_128 = 0x100000000000000000000000000000000; + let value_felt252 = low_felt252 + high_felt252 * two_pow_128; + + let unsafe_value = u256_from_felt252(value_felt252); + assert(val == unsafe_value, 'Value too large for felt'); + + value_felt252 +} diff --git a/warplib/src/lib.cairo b/warplib/src/lib.cairo index 3e1bd66dd..ccf4a183f 100644 --- a/warplib/src/lib.cairo +++ b/warplib/src/lib.cairo @@ -2,3 +2,4 @@ mod integer; mod maths; mod warp_memory; +mod memory; diff --git a/warplib/src/memory.cairo b/warplib/src/memory.cairo index eb8f0daed..675f27f15 100644 --- a/warplib/src/memory.cairo +++ b/warplib/src/memory.cairo @@ -1,18 +1,3 @@ -from starkware.cairo.common.alloc import alloc -from starkware.cairo.common.dict import dict_read, dict_write -from starkware.cairo.common.dict_access import DictAccess -from starkware.cairo.common.math import split_felt -from starkware.cairo.common.math_cmp import is_le -from starkware.cairo.common.uint256 import ( - Uint256, - uint256_add, - uint256_sub, - uint256_le, - uint256_lt, - uint256_mul, -) -from warplib.maths.utils import felt_to_uint256, narrow_safe - // =================================THE PLAN================================= // Memory needs to be able to handle the following types: // Scalars @@ -33,682 +18,99 @@ from warplib.maths.utils import felt_to_uint256, narrow_safe // =========================================================================== -// -----------------Scalars----------------- - -func wm_read_felt{warp_memory: DictAccess*}(loc: felt) -> (val: felt) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (res,); -} - -func wm_read_8{warp_memory: DictAccess*}(loc: felt) -> (val: u8) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u8_from_felt(res),); -} - -func wm_read_16{warp_memory: DictAccess*}(loc: felt) -> (val: u16) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u16_from_felt(res),); -} - -func wm_read_24{warp_memory: DictAccess*}(loc: felt) -> (val: u24) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u24_from_felt(res),); -} - -func wm_read_32{warp_memory: DictAccess*}(loc: felt) -> (val: u32) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u32_from_felt(res),); -} - -func wm_read_40{warp_memory: DictAccess*}(loc: felt) -> (val: u40) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u40_from_felt(res),); -} - -func wm_read_48{warp_memory: DictAccess*}(loc: felt) -> (val: u48) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u48_from_felt(res),); -} - -func wm_read_56{warp_memory: DictAccess*}(loc: felt) -> (val: u56) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u56_from_felt(res),); -} - -func wm_read_64{warp_memory: DictAccess*}(loc: felt) -> (val: u64) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u64_from_felt(res),); -} - -func wm_read_72{warp_memory: DictAccess*}(loc: felt) -> (val: u72) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u72_from_felt(res),); -} - -func wm_read_80{warp_memory: DictAccess*}(loc: felt) -> (val: u80) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u80_from_felt(res),); -} - -func wm_read_88{warp_memory: DictAccess*}(loc: felt) -> (val: u88) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u88_from_felt(res),); -} - -func wm_read_96{warp_memory: DictAccess*}(loc: felt) -> (val: u96) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u96_from_felt(res),); -} - -func wm_read_104{warp_memory: DictAccess*}(loc: felt) -> (val: u104) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u104_from_felt(res),); -} - -func wm_read_112{warp_memory: DictAccess*}(loc: felt) -> (val: u112) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u112_from_felt(res),); -} - -func wm_read_120{warp_memory: DictAccess*}(loc: felt) -> (val: u120) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u120_from_felt(res),); -} - -func wm_read_128{warp_memory: DictAccess*}(loc: felt) -> (val: u128) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u128_from_felt(res),); -} - -func wm_read_136{warp_memory: DictAccess*}(loc: felt) -> (val: u136) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u136_from_felt(res),); -} - -func wm_read_144{warp_memory: DictAccess*}(loc: felt) -> (val: u144) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u144_from_felt(res),); -} +use integer::u256_from_felt252; +use integer::u128_to_felt252; +use warplib::integer::u256_from_felts; +use warplib::integer::u256_to_felt252_safe; +use warplib::warp_memory::WarpMemory; +use warplib::warp_memory::MemoryTrait; +use warplib::warp_memory::WarpMemoryImpl; -func wm_read_152{warp_memory: DictAccess*}(loc: felt) -> (val: u152) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u152_from_felt(res),); -} - -func wm_read_160{warp_memory: DictAccess*}(loc: felt) -> (val: u160) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u160_from_felt(res),); -} - -func wm_read_168{warp_memory: DictAccess*}(loc: felt) -> (val: u168) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u168_from_felt(res),); -} - -func wm_read_176{warp_memory: DictAccess*}(loc: felt) -> (val: u176) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u176_from_felt(res),); -} - -func wm_read_184{warp_memory: DictAccess*}(loc: felt) -> (val: u184) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u184_from_felt(res),); -} - -func wm_read_192{warp_memory: DictAccess*}(loc: felt) -> (val: u192) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u192_from_felt(res),); -} - -func wm_read_200{warp_memory: DictAccess*}(loc: felt) -> (val: u200) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u200_from_felt(res),); -} - -func wm_read_208{warp_memory: DictAccess*}(loc: felt) -> (val: u208) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u208_from_felt(res),); -} - -func wm_read_216{warp_memory: DictAccess*}(loc: felt) -> (val: u216) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u216_from_felt(res),); -} - -func wm_read_224{warp_memory: DictAccess*}(loc: felt) -> (val: u224) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u224_from_felt(res),); -} - -func wm_read_232{warp_memory: DictAccess*}(loc: felt) -> (val: u232) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u232_from_felt(res),); -} - -func wm_read_240{warp_memory: DictAccess*}(loc: felt) -> (val: u240) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u240_from_felt(res),); -} +// -----------------Scalars----------------- -func wm_read_248{warp_memory: DictAccess*}(loc: felt) -> (val: u248) { - let (res) = dict_read{dict_ptr=warp_memory}(loc); - return (u248_from_felt(res),); +#[implicit(warp_memory: WarpMemory)] +fn wm_read_256(loc: felt252) -> u256 { + // TODO Clean this warp_memory initialization once plugin is supported + let mut warp_memory: WarpMemory = WarpMemoryImpl::initialize(); + + let low = warp_memory.get(loc); + let high = warp_memory.get(loc + 1); + u256_from_felts(low, high) } -func wm_read_256{warp_memory: DictAccess*}(loc: felt) -> (val: u256) { - let (low) = dict_read{dict_ptr=warp_memory}(loc); - let (high) = dict_read{dict_ptr=warp_memory}(loc + 1); - return (Uint256(low, high),); -} +#[implicit(warp_memory: WarpMemory)] +fn wm_write_256(loc: felt252, value: u256) -> u256 { + // TODO Clean this warp_memory initialization once plugin is supported + let mut warp_memory: WarpMemory = WarpMemoryImpl::initialize(); -func wm_write_felt{warp_memory: DictAccess*}(loc: felt, value: felt) -> (res: felt) { - dict_write{dict_ptr=warp_memory}(loc, value); - return (value,); -} - - -func wm_write_8{warp_memory: DictAccess*}(loc: felt, value: u8) -> (res: u8) { - let (fvalue) = u8_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_16{warp_memory: DictAccess*}(loc: felt, value: u16) -> (res: u16) { - let (fvalue) = u16_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_24{warp_memory: DictAccess*}(loc: felt, value: u24) -> (res: u24) { - let (fvalue) = u24_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_32{warp_memory: DictAccess*}(loc: felt, value: u32) -> (res: u32) { - let (fvalue) = u32_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_40{warp_memory: DictAccess*}(loc: felt, value: u40) -> (res: u40) { - let (fvalue) = u40_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_48{warp_memory: DictAccess*}(loc: felt, value: u48) -> (res: u48) { - let (fvalue) = u48_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_56{warp_memory: DictAccess*}(loc: felt, value: u56) -> (res: u56) { - let (fvalue) = u56_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_64{warp_memory: DictAccess*}(loc: felt, value: u64) -> (res: u64) { - let (fvalue) = u64_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_72{warp_memory: DictAccess*}(loc: felt, value: u72) -> (res: u72) { - let (fvalue) = u72_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_80{warp_memory: DictAccess*}(loc: felt, value: u80) -> (res: u80) { - let (fvalue) = u80_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_88{warp_memory: DictAccess*}(loc: felt, value: u88) -> (res: u88) { - let (fvalue) = u88_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_96{warp_memory: DictAccess*}(loc: felt, value: u96) -> (res: u96) { - let (fvalue) = u96_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_104{warp_memory: DictAccess*}(loc: felt, value: u104) -> (res: u104) { - let (fvalue) = u104_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_112{warp_memory: DictAccess*}(loc: felt, value: u112) -> (res: u112) { - let (fvalue) = u112_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_120{warp_memory: DictAccess*}(loc: felt, value: u120) -> (res: u120) { - let (fvalue) = u120_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_128{warp_memory: DictAccess*}(loc: felt, value: u128) -> (res: u128) { - let (fvalue) = u128_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_136{warp_memory: DictAccess*}(loc: felt, value: u136) -> (res: u136) { - let (fvalue) = u136_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_144{warp_memory: DictAccess*}(loc: felt, value: u144) -> (res: u144) { - let (fvalue) = u144_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_152{warp_memory: DictAccess*}(loc: felt, value: u152) -> (res: u152) { - let (fvalue) = u152_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_160{warp_memory: DictAccess*}(loc: felt, value: u160) -> (res: u160) { - let (fvalue) = u160_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_168{warp_memory: DictAccess*}(loc: felt, value: u168) -> (res: u168) { - let (fvalue) = u168_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_176{warp_memory: DictAccess*}(loc: felt, value: u176) -> (res: u176) { - let (fvalue) = u176_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_184{warp_memory: DictAccess*}(loc: felt, value: u184) -> (res: u184) { - let (fvalue) = u184_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_192{warp_memory: DictAccess*}(loc: felt, value: u192) -> (res: u192) { - let (fvalue) = u192_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_200{warp_memory: DictAccess*}(loc: felt, value: u200) -> (res: u200) { - let (fvalue) = u200_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_208{warp_memory: DictAccess*}(loc: felt, value: u208) -> (res: u208) { - let (fvalue) = u208_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_216{warp_memory: DictAccess*}(loc: felt, value: u216) -> (res: u216) { - let (fvalue) = u216_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_224{warp_memory: DictAccess*}(loc: felt, value: u224) -> (res: u224) { - let (fvalue) = u224_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_232{warp_memory: DictAccess*}(loc: felt, value: u232) -> (res: u232) { - let (fvalue) = u232_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_240{warp_memory: DictAccess*}(loc: felt, value: u240) -> (res: u240) { - let (fvalue) = u240_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} -func wm_write_248{warp_memory: DictAccess*}(loc: felt, value: u248) -> (res: u248) { - let (fvalue) = u248_to_felt(value); - dict_write{dict_ptr=warp_memory}(loc, fvalue); - return (value,); -} - -func wm_write_256{warp_memory: DictAccess*}(loc: felt, value: Uint256) -> (res: Uint256) { - dict_write{dict_ptr=warp_memory}(loc, value.low); - dict_write{dict_ptr=warp_memory}(loc + 1, value.high); - return (value,); + warp_memory.insert(loc, u128_to_felt252(value.low)); + warp_memory.insert(loc + 1, u128_to_felt252(value.high)); + value } // -----------------Arrays----------------- -func wm_index_static{range_check_ptr}( - arrayLoc: felt, index: Uint256, width: Uint256, length: Uint256 -) -> (loc: felt) { - // Check that the array index is valid - let (inRange) = uint256_lt(index, length); - assert inRange = 1; - - // Multiply index by element width to calculate felt offset - let (offset: Uint256, overflow: Uint256) = uint256_mul(index, width); - assert overflow.low = 0; - assert overflow.high = 0; - - // Add felt offset to address of array to get address of element - let (arrayLoc256: Uint256) = felt_to_uint256(arrayLoc); - let (res: Uint256, carry: felt) = uint256_add(arrayLoc256, offset); - assert carry = 0; - - // Safely narrow back to felt - let (loc: felt) = narrow_safe(res); - return (loc,); -} +#[implicit(warp_memory: WarpMemory)] +fn wm_index_dyn(arrayLoc: felt252, index: u256, width: u256) -> felt252 { + // TODO Clean this warp_memory initialization once plugin is supported + let mut warp_memory: WarpMemory = WarpMemoryImpl::initialize(); -func wm_index_dyn{range_check_ptr, warp_memory: DictAccess*}( - arrayLoc: felt, index: Uint256, width: Uint256 -) -> (loc: felt) { - alloc_locals; // Get the length of the array and check that the index is within bounds - let (length: Uint256) = wm_read_256(arrayLoc); - let (inRange) = uint256_lt(index, length); - assert inRange = 1; + assert(index < wm_dyn_array_length(arrayLoc), 'Index out of bound'); // Calculate the location of the element - let (offset: Uint256, overflow: Uint256) = uint256_mul(index, width); - assert overflow.low = 0; - assert overflow.high = 0; + let offset: u256 = index * width; + let element_zero_ptr: u256 = u256_from_felt252(arrayLoc + 2); + let loc = u256_to_felt252_safe(element_zero_ptr + offset); - let (elementZeroPtr) = felt_to_uint256(arrayLoc + 2); - let (res256: Uint256, carry) = uint256_add(elementZeroPtr, offset); - assert carry = 0; - let (res) = narrow_safe(res256); - - return (res,); + loc } -func wm_new{range_check_ptr, warp_memory: DictAccess*}(len: Uint256, elemWidth: Uint256) -> ( - loc: felt -) { - alloc_locals; +#[implicit(warp_memory: WarpMemory)] +fn wm_new(len: u256, elemWidth: u256) -> felt252 { + // TODO Clean this warp_memory initialization once plugin is supported + let mut warp_memory: WarpMemory = WarpMemoryImpl::initialize(); + // Calculate space needed for array elements - let (feltLength: Uint256, overflow: Uint256) = uint256_mul(len, elemWidth); - assert overflow.low = 0; - assert overflow.high = 0; + let feltLength = len * elemWidth; // Add space required to include the length member - let (feltLength: Uint256, carry: felt) = uint256_add(feltLength, Uint256(2, 0)); - assert carry = 0; - - let (loc) = wm_alloc(feltLength); - dict_write{dict_ptr=warp_memory}(loc, len.low); - dict_write{dict_ptr=warp_memory}(loc + 1, len.high); - return (loc,); -} - -func wm_dyn_array_length{warp_memory: DictAccess*}(arrayLoc: felt) -> (len: Uint256) { - let (low) = dict_read{dict_ptr=warp_memory}(arrayLoc); - let (high) = dict_read{dict_ptr=warp_memory}(arrayLoc + 1); - return (Uint256(low, high),); -} - -// The wm_bytes methods below are not currently in use since solidity dynamic -// memory arrays do not support `push` and `pop` operations. They are kept with -// the expectation that they will likely be added to solidity soon. -func wm_bytes_new{range_check_ptr, warp_memory: DictAccess*}(len: Uint256) -> (loc: felt) { - alloc_locals; - // Create an array to hold the bytes - let (arrayLoc) = wm_alloc(len); - - // Create a location with the metadata for the bytesLoc - // Size is five for (lenght: Uint256, capacity: Uint256, ptr: felt) - let (loc) = wm_alloc(Uint256(5, 0)); - dict_write{dict_ptr=warp_memory}(loc, len.low); - dict_write{dict_ptr=warp_memory}(loc + 1, len.high); - dict_write{dict_ptr=warp_memory}(loc + 2, len.low); - dict_write{dict_ptr=warp_memory}(loc + 3, len.high); - dict_write{dict_ptr=warp_memory}(loc + 4, arrayLoc); - - return (loc,); -} - -func wm_bytes_push{range_check_ptr, warp_memory: DictAccess*}(bytesLoc: felt, value: felt) -> ( - len: Uint256 -) { - alloc_locals; - - // Compute the new length - let (length) = wm_read_256(bytesLoc); - let (newLength, carry) = uint256_add(length, Uint256(1, 0)); - assert carry = 0; - - // Update the length in memory - dict_write{dict_ptr=warp_memory}(bytesLoc, newLength.low); - dict_write{dict_ptr=warp_memory}(bytesLoc + 1, newLength.high); - - let (capacity) = wm_read_256(bytesLoc + 2); - let (arrayLoc) = wm_read_felt(bytesLoc + 4); + let feltLength = feltLength + u256_from_felt252(2); - // Check our memory array has enough capacity - let (le) = uint256_lt(length, capacity); - if (le == 1) { - // Add length to address of array to get address of the new slot - let (arrayLoc256) = felt_to_uint256(arrayLoc); - let (res: Uint256, carry: felt) = uint256_add(arrayLoc256, length); - assert carry = 0; - // Safely narrow back to felt - let (loc: felt) = narrow_safe(res); - // Write the new value - dict_write{dict_ptr=warp_memory}(loc, value); - } else { - // Double the capacity - let (newCapacity, mulCarry) = uint256_mul(capacity, Uint256(2, 0)); - // This assert isn't perfect, if the old capacity is >= 2^255 this will - // fail while there may still be plenty of capacity left. However there - // is no computer capable of hitting this bound so I'm happy to fail - // early - assert mulCarry = Uint256(0, 0); - let (newArrayLoc) = wm_alloc(newCapacity); - - // Copy the old array to the new one - let (len) = narrow_safe(length); - wm_copy(arrayLoc, newArrayLoc, len); - - // Update the capacity in memory - dict_write{dict_ptr=warp_memory}(bytesLoc + 2, newCapacity.low); - dict_write{dict_ptr=warp_memory}(bytesLoc + 3, newCapacity.high); - // Update the array pointer - dict_write{dict_ptr=warp_memory}(bytesLoc + 4, newArrayLoc); - - // Add length to address of array to get address of the new slot - let (arrayLoc256) = felt_to_uint256(newArrayLoc); - let (res: Uint256, carry: felt) = uint256_add(arrayLoc256, length); - assert carry = 0; - // Safely narrow back to felt - let (loc: felt) = narrow_safe(res); - - // Write the new value - dict_write{dict_ptr=warp_memory}(loc, value); - } - return (newLength,); -} - -func wm_bytes_pop{range_check_ptr, warp_memory: DictAccess*}(bytesLoc: felt) -> ( - value: felt, len: Uint256 -) { - alloc_locals; - let (length) = wm_read_256(bytesLoc); - // Assert the pop operation is not on an empty array - if (length.low + length.high == 0) { - assert 1 = 0; - } - - // Compute the new length - let (newLength) = uint256_sub(length, Uint256(1, 0)); - - // Read the popped value - - // Read the pointer to array - let (arrayLoc) = wm_read_felt(bytesLoc + 4); - // Add newLength to address of array to get address of the tail - let (arrayLoc256) = felt_to_uint256(arrayLoc); - let (res: Uint256, carry: felt) = uint256_add(arrayLoc256, newLength); - assert carry = 0; - // Safely narrow back to felt - let (loc: felt) = narrow_safe(res); - - // Read the value - let (value) = dict_read{dict_ptr=warp_memory}(loc); - - // Write the new length - dict_write{dict_ptr=warp_memory}(bytesLoc, newLength.low); - dict_write{dict_ptr=warp_memory}(bytesLoc + 1, newLength.high); - - return (value, newLength); -} - -func wm_bytes_index{range_check_ptr, warp_memory: DictAccess*}(bytesLoc: felt, index: Uint256) -> ( - res: felt -) { - alloc_locals; - - // Get the arrayLoc - let (arrayLoc) = wm_read_felt(bytesLoc + 4); - - // Get the length of the array and check that the index is within bounds - let (length: Uint256) = wm_read_256(bytesLoc); - let (inRange) = uint256_lt(index, length); - assert inRange = 1; - - let (arrayLoc256) = felt_to_uint256(arrayLoc); - let (res256: Uint256, carry) = uint256_add(arrayLoc256, index); - assert carry = 0; - let (res) = narrow_safe(res256); - - return (res,); + let empty_memory_start = wm_alloc(feltLength); + wm_write_256(empty_memory_start, len); + empty_memory_start } -func wm_bytes_length{warp_memory: DictAccess*}(bytesLoc: felt) -> (len: Uint256) { - let (res: Uint256) = wm_read_256(bytesLoc); - return (res,); -} - -func wm_bytes_to_fixed32{range_check_ptr, warp_memory: DictAccess*}(bytesLoc: felt) -> ( - res: Uint256 -) { - alloc_locals; - let (dataLength) = wm_read_256(bytesLoc); - if (dataLength.high == 0) { - let (high) = wm_bytes_to_fixed_helper(bytesLoc + 2, 16, dataLength.low, 0); - let short = is_le(dataLength.low, 16); - if (short == 0) { - let (low) = wm_bytes_to_fixed_helper(bytesLoc + 18, 16, dataLength.low - 16, 0); - return (Uint256(low, high),); - } else { - return (Uint256(0, high),); - } - } else { - let (high) = wm_bytes_to_fixed_helper(bytesLoc + 2, 16, 16, 0); - let (low) = wm_bytes_to_fixed_helper(bytesLoc + 18, 16, 16, 0); - return (Uint256(low, high),); - } -} - -func wm_bytes_to_fixed{warp_memory: DictAccess*}(bytesLoc: felt, width: felt) -> (res: felt) { - alloc_locals; - let (dataLength) = wm_read_256(bytesLoc); - if (dataLength.high == 0) { - return wm_bytes_to_fixed_helper(bytesLoc + 2, width, dataLength.low, 0); - } else { - return wm_bytes_to_fixed_helper(bytesLoc + 2, width, width, 0); - } +#[implicit(warp_memory: WarpMemory)] +fn wm_dyn_array_length(arrayLoc: felt252) -> u256 { + // TODO Clean this warp_memory initialization once plugin is supported + let mut warp_memory: WarpMemory = WarpMemoryImpl::initialize(); + + wm_read_256(arrayLoc) } // -----------------Structs----------------- -func index_struct(loc: felt, index: felt) -> (indexLoc: felt) { - // No need to range check here, that was already done when the struct was allocated - return (loc + index,); -} // -----------------Helper functions----------------- -// Returns an existing pointer to a reference data type structure. If it does not exist, it will -// create a new pointer -func wm_read_id{range_check_ptr: felt, warp_memory: DictAccess*}(loc: felt, size: Uint256) -> ( - val: felt -) { - let (id) = dict_read{dict_ptr=warp_memory}(loc); - if (id != 0) { - return (id,); - } - let (id) = wm_alloc(size); - dict_write{dict_ptr=warp_memory}(loc, id); - return (id,); -} // Moves the free-memory pointer to allocate the given number of cells, and returns the index // of the start of the allocated space -func wm_alloc{range_check_ptr, warp_memory: DictAccess*}(space: Uint256) -> (start: felt) { - alloc_locals; - // Get current end pointer - let (freeCell) = dict_read{dict_ptr=warp_memory}(0); - - // Widen to uint256 for safe calculation and because array lengths are uint256 - let (freeCell256) = felt_to_uint256(freeCell); - let (newFreeCell256: Uint256, carry) = uint256_add(freeCell256, space); - assert carry = 0; - let (newFreeCell) = narrow_safe(newFreeCell256); - dict_write{dict_ptr=warp_memory}(0, newFreeCell); - return (freeCell,); -} - -// Copies length felts from src to dst -func wm_copy{warp_memory: DictAccess*}(src: felt, dst: felt, length: felt) { - alloc_locals; - if (length == 0) { - return (); - } - - let (srcVal) = dict_read{dict_ptr=warp_memory}(src); - dict_write{dict_ptr=warp_memory}(dst, srcVal); +#[implicit(warp_memory: WarpMemory)] +fn wm_alloc(space: u256) -> felt252 { + // TODO Clean this warp_memory initialization once warp-memory plugin is supported + let mut warp_memory: WarpMemory = WarpMemoryImpl::initialize(); - wm_copy(src + 1, dst + 1, length - 1); - return (); -} - -// Converts an array in memory to a felt array -func wm_to_felt_array{range_check_ptr, warp_memory: DictAccess*}(loc: felt) -> ( - length: felt, output: felt* -) { - alloc_locals; - let (output: felt*) = alloc(); - - let (lengthUint256: Uint256) = wm_read_256(loc); - let (length_felt: felt) = narrow_safe(lengthUint256); - - wm_to_felt_array_helper(loc + 2, 0, length_felt, output); - - return (length_felt, output); -} - -func wm_to_felt_array_helper{range_check_ptr, warp_memory: DictAccess*}( - loc: felt, index: felt, length: felt, output: felt* -) { - alloc_locals; - if (index == length) { - return (); - } + // Get current end pointer + let freeCell = warp_memory.pointer; - let (value: felt) = dict_read{dict_ptr=warp_memory}(loc); - assert output[index] = value; + // Widen to u256 for safe calculation and because array lengths are u256 + let freeCell256 = u256_from_felt252(freeCell); + let newFreeCell256 = freeCell256 + space; - return wm_to_felt_array_helper(loc + 1, index + 1, length, output); + warp_memory.pointer = u256_to_felt252_safe(newFreeCell256); + freeCell } -func wm_bytes_to_fixed_helper{warp_memory: DictAccess*}( - bytesDataLoc: felt, targetWidth: felt, dataLength: felt, acc: felt -) -> (res: felt) { - alloc_locals; - if (targetWidth == 0) { - return (acc,); - } - if (dataLength == 0) { - return wm_bytes_to_fixed_helper( - bytesDataLoc + 1, targetWidth - 1, dataLength - 1, 256 * acc - ); - } else { - let (byte) = wm_read_felt(bytesDataLoc); - return wm_bytes_to_fixed_helper( - bytesDataLoc + 1, targetWidth - 1, dataLength - 1, 256 * acc + byte - ); - } -} diff --git a/warplib/src/memory.old.cairo b/warplib/src/memory.old.cairo new file mode 100644 index 000000000..60a4f6513 --- /dev/null +++ b/warplib/src/memory.old.cairo @@ -0,0 +1,406 @@ +// TODO This script holds the old cairo0 definition of memory util functions. Functions +// that are needed from here should be added in `memory.cairo` and translated. +// This file should be removed once we finish the transition to Cairo1 + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.dict import dict_read, dict_write +from starkware.cairo.common.dict_access import DictAccess +from starkware.cairo.common.math import split_felt +from starkware.cairo.common.math_cmp import is_le +from starkware.cairo.common.uint256 import ( + Uint256, + uint256_add, + uint256_sub, + uint256_le, + uint256_lt, + uint256_mul, +) +from warplib.maths.utils import felt_to_uint256, narrow_safe + +// =================================THE PLAN================================= +// Memory needs to be able to handle the following types: +// Scalars +// Ints, bools, addresses, etc +// Arrays +// Static, and dynamic +// Structs + +// Scalars are easy, they fit in one or two felts each, and read functions +// can be prewritten here + +// Static arrays are just elements laid out consecutively in memory + +// Dynamic arrays are harder, they are stored as felts that 'point' to an +// allocated data space that contains first the length and then the data +// This means that they can have a known size when being packed into a +// compound type but can still have a number of elements known only at runtime + +// =========================================================================== + +// -----------------Scalars----------------- + +func wm_read_felt{warp_memory: DictAccess*}(loc: felt) -> (val: felt) { + let (res) = dict_read{dict_ptr=warp_memory}(loc); + return (res,); +} + +func wm_read_256{warp_memory: DictAccess*}(loc: felt) -> (val: Uint256) { + let (low) = dict_read{dict_ptr=warp_memory}(loc); + let (high) = dict_read{dict_ptr=warp_memory}(loc + 1); + return (Uint256(low, high),); +} + +func wm_write_felt{warp_memory: DictAccess*}(loc: felt, value: felt) -> (res: felt) { + dict_write{dict_ptr=warp_memory}(loc, value); + return (value,); +} + +func wm_write_256{warp_memory: DictAccess*}(loc: felt, value: Uint256) -> (res: Uint256) { + dict_write{dict_ptr=warp_memory}(loc, value.low); + dict_write{dict_ptr=warp_memory}(loc + 1, value.high); + return (value,); +} + +// -----------------Arrays----------------- + +func wm_index_static{range_check_ptr}( + arrayLoc: felt, index: Uint256, width: Uint256, length: Uint256 +) -> (loc: felt) { + // Check that the array index is valid + let (inRange) = uint256_lt(index, length); + assert inRange = 1; + + // Multiply index by element width to calculate felt offset + let (offset: Uint256, overflow: Uint256) = uint256_mul(index, width); + assert overflow.low = 0; + assert overflow.high = 0; + + // Add felt offset to address of array to get address of element + let (arrayLoc256: Uint256) = felt_to_uint256(arrayLoc); + let (res: Uint256, carry: felt) = uint256_add(arrayLoc256, offset); + assert carry = 0; + + // Safely narrow back to felt + let (loc: felt) = narrow_safe(res); + return (loc,); +} + +func wm_index_dyn{range_check_ptr, warp_memory: DictAccess*}( + arrayLoc: felt, index: Uint256, width: Uint256 +) -> (loc: felt) { + alloc_locals; + // Get the length of the array and check that the index is within bounds + let (length: Uint256) = wm_read_256(arrayLoc); + let (inRange) = uint256_lt(index, length); + assert inRange = 1; + + // Calculate the location of the element + let (offset: Uint256, overflow: Uint256) = uint256_mul(index, width); + assert overflow.low = 0; + assert overflow.high = 0; + + let (elementZeroPtr) = felt_to_uint256(arrayLoc + 2); + let (res256: Uint256, carry) = uint256_add(elementZeroPtr, offset); + assert carry = 0; + let (res) = narrow_safe(res256); + + return (res,); +} + +func wm_new{range_check_ptr, warp_memory: DictAccess*}(len: Uint256, elemWidth: Uint256) -> ( + loc: felt +) { + alloc_locals; + // Calculate space needed for array elements + let (feltLength: Uint256, overflow: Uint256) = uint256_mul(len, elemWidth); + assert overflow.low = 0; + assert overflow.high = 0; + + // Add space required to include the length member + let (feltLength: Uint256, carry: felt) = uint256_add(feltLength, Uint256(2, 0)); + assert carry = 0; + + let (loc) = wm_alloc(feltLength); + dict_write{dict_ptr=warp_memory}(loc, len.low); + dict_write{dict_ptr=warp_memory}(loc + 1, len.high); + return (loc,); +} + +func wm_dyn_array_length{warp_memory: DictAccess*}(arrayLoc: felt) -> (len: Uint256) { + let (low) = dict_read{dict_ptr=warp_memory}(arrayLoc); + let (high) = dict_read{dict_ptr=warp_memory}(arrayLoc + 1); + return (Uint256(low, high),); +} + +// The wm_bytes methods below are not currently in use since solidity dynamic +// memory arrays do not support `push` and `pop` operations. They are kept with +// the expectation that they will likely be added to solidity soon. +func wm_bytes_new{range_check_ptr, warp_memory: DictAccess*}(len: Uint256) -> (loc: felt) { + alloc_locals; + // Create an array to hold the bytes + let (arrayLoc) = wm_alloc(len); + + // Create a location with the metadata for the bytesLoc + // Size is five for (lenght: Uint256, capacity: Uint256, ptr: felt) + let (loc) = wm_alloc(Uint256(5, 0)); + dict_write{dict_ptr=warp_memory}(loc, len.low); + dict_write{dict_ptr=warp_memory}(loc + 1, len.high); + dict_write{dict_ptr=warp_memory}(loc + 2, len.low); + dict_write{dict_ptr=warp_memory}(loc + 3, len.high); + dict_write{dict_ptr=warp_memory}(loc + 4, arrayLoc); + + return (loc,); +} + +func wm_bytes_push{range_check_ptr, warp_memory: DictAccess*}(bytesLoc: felt, value: felt) -> ( + len: Uint256 +) { + alloc_locals; + + // Compute the new length + let (length) = wm_read_256(bytesLoc); + let (newLength, carry) = uint256_add(length, Uint256(1, 0)); + assert carry = 0; + + // Update the length in memory + dict_write{dict_ptr=warp_memory}(bytesLoc, newLength.low); + dict_write{dict_ptr=warp_memory}(bytesLoc + 1, newLength.high); + + let (capacity) = wm_read_256(bytesLoc + 2); + let (arrayLoc) = wm_read_felt(bytesLoc + 4); + + // Check our memory array has enough capacity + let (le) = uint256_lt(length, capacity); + if (le == 1) { + // Add length to address of array to get address of the new slot + let (arrayLoc256) = felt_to_uint256(arrayLoc); + let (res: Uint256, carry: felt) = uint256_add(arrayLoc256, length); + assert carry = 0; + // Safely narrow back to felt + let (loc: felt) = narrow_safe(res); + // Write the new value + dict_write{dict_ptr=warp_memory}(loc, value); + } else { + // Double the capacity + let (newCapacity, mulCarry) = uint256_mul(capacity, Uint256(2, 0)); + // This assert isn't perfect, if the old capacity is >= 2^255 this will + // fail while there may still be plenty of capacity left. However there + // is no computer capable of hitting this bound so I'm happy to fail + // early + assert mulCarry = Uint256(0, 0); + let (newArrayLoc) = wm_alloc(newCapacity); + + // Copy the old array to the new one + let (len) = narrow_safe(length); + wm_copy(arrayLoc, newArrayLoc, len); + + // Update the capacity in memory + dict_write{dict_ptr=warp_memory}(bytesLoc + 2, newCapacity.low); + dict_write{dict_ptr=warp_memory}(bytesLoc + 3, newCapacity.high); + // Update the array pointer + dict_write{dict_ptr=warp_memory}(bytesLoc + 4, newArrayLoc); + + // Add length to address of array to get address of the new slot + let (arrayLoc256) = felt_to_uint256(newArrayLoc); + let (res: Uint256, carry: felt) = uint256_add(arrayLoc256, length); + assert carry = 0; + // Safely narrow back to felt + let (loc: felt) = narrow_safe(res); + + // Write the new value + dict_write{dict_ptr=warp_memory}(loc, value); + } + return (newLength,); +} + +func wm_bytes_pop{range_check_ptr, warp_memory: DictAccess*}(bytesLoc: felt) -> ( + value: felt, len: Uint256 +) { + alloc_locals; + let (length) = wm_read_256(bytesLoc); + // Assert the pop operation is not on an empty array + if (length.low + length.high == 0) { + assert 1 = 0; + } + + // Compute the new length + let (newLength) = uint256_sub(length, Uint256(1, 0)); + + // Read the popped value + + // Read the pointer to array + let (arrayLoc) = wm_read_felt(bytesLoc + 4); + // Add newLength to address of array to get address of the tail + let (arrayLoc256) = felt_to_uint256(arrayLoc); + let (res: Uint256, carry: felt) = uint256_add(arrayLoc256, newLength); + assert carry = 0; + // Safely narrow back to felt + let (loc: felt) = narrow_safe(res); + + // Read the value + let (value) = dict_read{dict_ptr=warp_memory}(loc); + + // Write the new length + dict_write{dict_ptr=warp_memory}(bytesLoc, newLength.low); + dict_write{dict_ptr=warp_memory}(bytesLoc + 1, newLength.high); + + return (value, newLength); +} + +func wm_bytes_index{range_check_ptr, warp_memory: DictAccess*}(bytesLoc: felt, index: Uint256) -> ( + res: felt +) { + alloc_locals; + + // Get the arrayLoc + let (arrayLoc) = wm_read_felt(bytesLoc + 4); + + // Get the length of the array and check that the index is within bounds + let (length: Uint256) = wm_read_256(bytesLoc); + let (inRange) = uint256_lt(index, length); + assert inRange = 1; + + let (arrayLoc256) = felt_to_uint256(arrayLoc); + let (res256: Uint256, carry) = uint256_add(arrayLoc256, index); + assert carry = 0; + let (res) = narrow_safe(res256); + + return (res,); +} + +func wm_bytes_length{warp_memory: DictAccess*}(bytesLoc: felt) -> (len: Uint256) { + let (res: Uint256) = wm_read_256(bytesLoc); + return (res,); +} + +func wm_bytes_to_fixed32{range_check_ptr, warp_memory: DictAccess*}(bytesLoc: felt) -> ( + res: Uint256 +) { + alloc_locals; + let (dataLength) = wm_read_256(bytesLoc); + if (dataLength.high == 0) { + let (high) = wm_bytes_to_fixed_helper(bytesLoc + 2, 16, dataLength.low, 0); + let short = is_le(dataLength.low, 16); + if (short == 0) { + let (low) = wm_bytes_to_fixed_helper(bytesLoc + 18, 16, dataLength.low - 16, 0); + return (Uint256(low, high),); + } else { + return (Uint256(0, high),); + } + } else { + let (high) = wm_bytes_to_fixed_helper(bytesLoc + 2, 16, 16, 0); + let (low) = wm_bytes_to_fixed_helper(bytesLoc + 18, 16, 16, 0); + return (Uint256(low, high),); + } +} + +func wm_bytes_to_fixed{warp_memory: DictAccess*}(bytesLoc: felt, width: felt) -> (res: felt) { + alloc_locals; + let (dataLength) = wm_read_256(bytesLoc); + if (dataLength.high == 0) { + return wm_bytes_to_fixed_helper(bytesLoc + 2, width, dataLength.low, 0); + } else { + return wm_bytes_to_fixed_helper(bytesLoc + 2, width, width, 0); + } +} + +// -----------------Structs----------------- + +func index_struct(loc: felt, index: felt) -> (indexLoc: felt) { + // No need to range check here, that was already done when the struct was allocated + return (loc + index,); +} + +// -----------------Helper functions----------------- + +// Returns an existing pointer to a reference data type structure. If it does not exist, it will +// create a new pointer +func wm_read_id{range_check_ptr: felt, warp_memory: DictAccess*}(loc: felt, size: Uint256) -> ( + val: felt +) { + let (id) = dict_read{dict_ptr=warp_memory}(loc); + if (id != 0) { + return (id,); + } + let (id) = wm_alloc(size); + dict_write{dict_ptr=warp_memory}(loc, id); + return (id,); +} + +// Moves the free-memory pointer to allocate the given number of cells, and returns the index +// of the start of the allocated space +func wm_alloc{range_check_ptr, warp_memory: DictAccess*}(space: Uint256) -> (start: felt) { + alloc_locals; + // Get current end pointer + let (freeCell) = dict_read{dict_ptr=warp_memory}(0); + + // Widen to uint256 for safe calculation and because array lengths are uint256 + let (freeCell256) = felt_to_uint256(freeCell); + let (newFreeCell256: Uint256, carry) = uint256_add(freeCell256, space); + assert carry = 0; + let (newFreeCell) = narrow_safe(newFreeCell256); + dict_write{dict_ptr=warp_memory}(0, newFreeCell); + return (freeCell,); +} + +// Copies length felts from src to dst +func wm_copy{warp_memory: DictAccess*}(src: felt, dst: felt, length: felt) { + alloc_locals; + if (length == 0) { + return (); + } + + let (srcVal) = dict_read{dict_ptr=warp_memory}(src); + dict_write{dict_ptr=warp_memory}(dst, srcVal); + + wm_copy(src + 1, dst + 1, length - 1); + return (); +} + +// Converts an array in memory to a felt array +func wm_to_felt_array{range_check_ptr, warp_memory: DictAccess*}(loc: felt) -> ( + length: felt, output: felt* +) { + alloc_locals; + let (output: felt*) = alloc(); + + let (lengthUint256: Uint256) = wm_read_256(loc); + let (length_felt: felt) = narrow_safe(lengthUint256); + + wm_to_felt_array_helper(loc + 2, 0, length_felt, output); + + return (length_felt, output); +} + +func wm_to_felt_array_helper{range_check_ptr, warp_memory: DictAccess*}( + loc: felt, index: felt, length: felt, output: felt* +) { + alloc_locals; + if (index == length) { + return (); + } + + let (value: felt) = dict_read{dict_ptr=warp_memory}(loc); + assert output[index] = value; + + return wm_to_felt_array_helper(loc + 1, index + 1, length, output); +} + +func wm_bytes_to_fixed_helper{warp_memory: DictAccess*}( + bytesDataLoc: felt, targetWidth: felt, dataLength: felt, acc: felt +) -> (res: felt) { + alloc_locals; + if (targetWidth == 0) { + return (acc,); + } + if (dataLength == 0) { + return wm_bytes_to_fixed_helper( + bytesDataLoc + 1, targetWidth - 1, dataLength - 1, 256 * acc + ); + } else { + let (byte) = wm_read_felt(bytesDataLoc); + return wm_bytes_to_fixed_helper( + bytesDataLoc + 1, targetWidth - 1, dataLength - 1, 256 * acc + byte + ); + } +} diff --git a/warplib/src/warp_memory.cairo b/warplib/src/warp_memory.cairo index 5e6ae6736..22a2f5733 100644 --- a/warplib/src/warp_memory.cairo +++ b/warplib/src/warp_memory.cairo @@ -18,6 +18,7 @@ trait MemoryTrait { fn initialize() -> WarpMemory; fn insert(ref self: WarpMemory, position: felt252, value: felt252); fn append(ref self: WarpMemory, value: felt252); + fn get(ref self: WarpMemory, key: felt252) -> felt252; } @@ -28,10 +29,14 @@ impl WarpMemoryImpl of MemoryTrait { fn insert(ref self: WarpMemory, position: felt252, value: felt252) { self.memory.insert(position, value); - self.pointer += 1; } fn append(ref self: WarpMemory, value: felt252) { self.insert(self.pointer, value); + self.pointer += 1; + } + + fn get(ref self: WarpMemory, key: felt252) -> felt252 { + self.memory.get(key) } }