Skip to content

Commit

Permalink
Merge pull request #7 from icanvardar/feat/#3-arithmetic
Browse files Browse the repository at this point in the history
Create ArithmeticLib, Add UnsignedInt and SignedInt Custom Types
  • Loading branch information
icanvardar authored Jul 27, 2024
2 parents 133480f + 72db6f0 commit afeee0a
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 1 deletion.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ All notable changes to this project will be documented in this file.
- Add BitmaskLib.sol
- *(Mask)* Add Mask custom type and its library
- *(BitmaskLib)* Implement updateDataWith
- Add ArithmeticLib library
- *(SignedInt)* Add custom type
- *(UnsignedInt)* Add custom type
- *(UnsignedInt)* Add UnsignedLib
- *(ArithmeticLib)* Add functions
- *(SignedInt)* Add functions

### 🐛 Bug Fixes

Expand All @@ -27,6 +33,7 @@ All notable changes to this project will be documented in this file.
- *(BitmaskLib)* Update Mask custom type
- *(BitmaskLib)* Update `build` function params
- *(BitmaskLib)* Remove `getLength` function
- *(UnsignedInt)* Update functions

### 📚 Documentation

Expand All @@ -35,6 +42,7 @@ All notable changes to this project will be documented in this file.

### 🎨 Styling

- *(SlotLib)* Update setUp function
- Move Mask to BitmaskLib
- *(BitmaskLib)* Rename updateDataWith as updateLeftPadded
- Format BitmaskLib.t.sol
Expand All @@ -56,5 +64,7 @@ All notable changes to this project will be documented in this file.
- Remove redundant contracts
- Update Gasgnome.sol
- Add CHANGELOG.md
- Update CHANGELOG.md
- Update Gasgnome.sol

<!-- generated by git-cliff -->
9 changes: 8 additions & 1 deletion src/Gasgnome.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.26;

import "./libraries/StorageLib.sol";
/// @notice Libraries
import "./libraries/ArithmeticLib.sol";
import "./libraries/BitmaskLib.sol";
import "./libraries/StorageLib.sol";

/// @notice Types
import "./types/SignedInt.sol";
import "./types/Slot.sol";
import "./types/UnsignedInt.sol";

library Gasgnome {
string internal constant AUTHOR = "https://github.com/icanvardar";
Expand Down
36 changes: 36 additions & 0 deletions src/libraries/ArithmeticLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.26;

library ArithmeticLib {
function convertWithSize(bytes32 b, uint16 sizeInBytes, uint8 sizeInBits) public pure returns (bytes32 to) {
assembly {
/// TODO: add custom error here - sizeInBytes cannot be zero/cannot be bigger than 256 bits
if or(eq(sizeInBytes, 0), gt(sizeInBytes, 0x100)) { revert(0x00, 0x00) }

/// TODO: add custom error here - sizeInBytes has to be multiple of eight
if gt(mod(sizeInBytes, 0x8), 0) { revert(0x00, 0x00) }
}

uint8 sizeCap = cap(sizeInBits);

assembly {
/// TODO: add custom error here - size cap cannot be bigger than given size in bytes
if gt(sizeCap, sizeInBytes) { revert(0x00, 0x00) }

to := b
}
}

/// NOTE: finds the nearest bit cap in multiple of eight
function cap(uint8 bitSize) public pure returns (uint8 res) {
assembly {
for { let i := 0x0 } lt(i, 0x21) { i := add(i, 1) } {
let tmp := mul(add(i, 1), 8)
if or(gt(tmp, bitSize), eq(tmp, bitSize)) {
res := tmp
break
}
}
}
}
}
98 changes: 98 additions & 0 deletions src/types/SignedInt.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.26;

import { ArithmeticLib } from "../libraries/ArithmeticLib.sol";

type SignedInt is bytes32;

using {
addSignedInt as +,
subSignedInt as -,
mulSignedInt as *,
divSignedInt as /,
modSignedInt as %,
expSignedInt as ^
} for SignedInt global;

function addSignedInt(SignedInt left, SignedInt right) pure returns (SignedInt res) {
assembly {
res := add(left, right)
}
}

function subSignedInt(SignedInt left, SignedInt right) pure returns (SignedInt res) {
assembly {
res := sub(left, right)
}
}

function mulSignedInt(SignedInt left, SignedInt right) pure returns (SignedInt res) {
assembly {
switch or(iszero(left), iszero(right))
case 0 { res := 0 }
case 1 { res := mul(left, right) }
}
}

function divSignedInt(SignedInt left, SignedInt right) pure returns (SignedInt res) {
assembly {
res := div(left, right)
}
}

function modSignedInt(SignedInt left, SignedInt right) pure returns (SignedInt res) {
assembly {
if iszero(right) {
/// TODO: add custom error here - modulo by zero check
revert(0x00, 0x00)
}

res := mod(left, right)
}
}

function expSignedInt(SignedInt left, SignedInt right) pure returns (SignedInt res) {
assembly {
res := exp(left, right)
}
}

using SignedIntLib for SignedInt global;

library SignedIntLib {
function convertWithSize(SignedInt u, uint16 sizeInBytes) public pure returns (int256 to) {
bytes32 result = ArithmeticLib.convertWithSize(SignedInt.unwrap(u), sizeInBytes, sizeInBits(u));

assembly {
to := result
}
}

function sizeInBits(SignedInt s) public pure returns (uint8 size) {
assembly {
let tmp
let isNegative := sgt(0, s)
let absValue := s
if isNegative { absValue := sub(0, s) }

for { let i := 0x1f } gt(i, 0x0) { i := sub(i, 1) } {
let b := byte(i, absValue)

/// Check existency of first nibble
let x := and(b, 0x0f)
if gt(x, 0) {
size := add(size, add(tmp, 0x1))
tmp := 0x0
}
if eq(x, 0) { tmp := add(tmp, 0x1) }

/// Check existency of second nibble
let y := and(b, 0xf0)
if gt(y, 0) { size := add(size, 0x1) }
if eq(y, 0) { tmp := add(tmp, 0x1) }
}

size := mul(size, 0x4)
}
}
}
109 changes: 109 additions & 0 deletions src/types/UnsignedInt.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.26;

import { ArithmeticLib } from "../libraries/ArithmeticLib.sol";

type UnsignedInt is bytes32;

using {
addUnsignedInt as +,
subUnsignedInt as -,
mulUnsignedInt as *,
divUnsignedInt as /,
modUnsignedInt as %,
expUnsignedInt as ^
} for UnsignedInt global;

function addUnsignedInt(UnsignedInt left, UnsignedInt right) pure returns (UnsignedInt res) {
assembly {
res := add(left, right)

if lt(res, left) {
/// TODO: add custom error here - overflow check
revert(0x00, 0x00)
}
}
}

function subUnsignedInt(UnsignedInt left, UnsignedInt right) pure returns (UnsignedInt res) {
assembly {
if gt(right, left) {
/// TODO: add custom error here - underflow check
revert(0x00, 0x00)
}

res := sub(left, right)
}
}

function mulUnsignedInt(UnsignedInt left, UnsignedInt right) pure returns (UnsignedInt res) {
assembly {
switch or(iszero(left), iszero(right))
case 0 { res := 0 }
case 1 { res := mul(left, right) }
}
}

function divUnsignedInt(UnsignedInt left, UnsignedInt right) pure returns (UnsignedInt res) {
assembly {
if eq(right, 0) {
/// TODO: add custom error here - division by zero check
revert(0x00, 0x00)
}

res := div(left, right)
}
}

function modUnsignedInt(UnsignedInt left, UnsignedInt right) pure returns (UnsignedInt res) {
assembly {
if iszero(right) {
/// TODO: add custom error here - modulo by zero check
revert(0x00, 0x00)
}

res := mod(left, right)
}
}

function expUnsignedInt(UnsignedInt left, UnsignedInt right) pure returns (UnsignedInt res) {
assembly {
res := exp(left, right)
}
}

using UnsignedIntLib for UnsignedInt global;

library UnsignedIntLib {
function convertWithSize(UnsignedInt u, uint16 sizeInBytes) public pure returns (uint256 to) {
bytes32 result = ArithmeticLib.convertWithSize(UnsignedInt.unwrap(u), sizeInBytes, sizeInBits(u));

assembly {
to := result
}
}

function sizeInBits(UnsignedInt u) public pure returns (uint8 size) {
assembly {
let tmp
for { let i := 0x1f } gt(i, 0x0) { i := sub(i, 1) } {
let b := byte(i, u)

/// NOTE: check existency of first nibble
let x := and(b, 0x0f)
if gt(x, 0) {
size := add(size, add(tmp, 0x1))
tmp := 0x0
}
if eq(x, 0) { tmp := add(tmp, 0x1) }

/// NOTE: check existency of second nibble
let y := and(b, 0xf0)
if gt(y, 0) { size := add(size, 0x1) }
if eq(y, 0) { tmp := add(tmp, 0x1) }
}

size := mul(size, 0x4)
}
}
}
7 changes: 7 additions & 0 deletions test/ArithmeticLib.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.26;

import { ArithmeticLib } from "../src/libraries/ArithmeticLib.sol";
import { Test, console } from "forge-std/Test.sol";

contract ArithmeticLibTest is Test { }

0 comments on commit afeee0a

Please sign in to comment.