Skip to content

Commit

Permalink
Add the U256 type (FuelLabs#2103)
Browse files Browse the repository at this point in the history
* feat: add new bare u256 module to std

* feat: Add base type + from,into traits

* test: setup testing for U256

* cleanup

* feat: add impl U256 max, min, bits, new

* test: add new test assertions

* style: fmt

* test: generate oracle file

* Update sway-lib-std/src/u256.sw

Co-authored-by: John Adler <[email protected]>

* fix: remove * import

* fix: improve error handling

* test: better test coverage

* style: fmt

* docs: add test comments

* test: add more test cases for to_u64()

* fix: remove #r prefix from storage lib

* test: remove redundant test

* refactor: rename to_u64 to as_u64

* Revert "fix: remove #r prefix from storage lib"

This reverts commit 8dd0738.

Co-authored-by: John Adler <[email protected]>
  • Loading branch information
nfurfaro and adlerjohn authored Jun 28, 2022
1 parent e06e993 commit 1a64db9
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 0 deletions.
1 change: 1 addition & 0 deletions sway-lib-std/src/lib.sw
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dep reentrancy;
dep vm/mod;
dep flags;
dep u128;
dep u256;
dep vec;

use core::*;
90 changes: 90 additions & 0 deletions sway-lib-std/src/u256.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
library u256;

use core::num::*;
use ::result::Result;

/// The 256-bit unsigned integer type.
/// Represented as four 64-bit components: `(a, b, c, d)`, where `value = (a << 192) + (b << 128) + (c << 64) + d`.
pub struct U256 {
a: u64,
b: u64,
c: u64,
d: u64,
}

pub enum U256Error {
LossOfPrecision: (),
}

pub trait From {
/// Function for creating a U256 from its u64 components.
pub fn from(a: u64, b: u64, c: u64, d: u64) -> Self;
} {
/// Function for extracting 4 u64s from a U256.
fn into(val: U256) -> (u64, u64, u64, u64) {
(val.a, val.b, val.c, val.d)
}
}

impl From for U256 {
pub fn from(a: u64, b: u64, c: u64, d: u64) -> U256 {
U256 {
a, b, c, d,
}
}
}

impl core::ops::Eq for U256 {
/// Function for comparing 2 U256s for equality
pub fn eq(self, other: Self) -> bool {
self.a == other.a && self.b == other.b && self.c == other.c && self.d == other.d
}
}

impl U256 {
/// Initializes a new, zeroed U256.
pub fn new() -> U256 {
U256 {
a: 0,
b: 0,
c: 0,
d: 0,
}
}

/// Safely downcast to `u64` without loss of precision.
/// Returns Err if the number > ~u64::max()
pub fn as_u64(self) -> Result<u64, U256Error> {
if self.a == 0 && self.b == 0 && self.c == 0 {
Result::Ok(self.d)
} else {
Result::Err(U256Error::LossOfPrecision)
}
}

/// The smallest value that can be represented by this integer type.
pub fn min() -> U256 {
U256 {
a: 0,
b: 0,
c: 0,
d: 0,
}
}

/// The largest value that can be represented by this type,
/// 2<sup>256</sup> - 1.
pub fn max() -> U256 {
U256 {
a: ~u64::max(),
b: ~u64::max(),
c: ~u64::max(),
d: ~u64::max(),
}
}

/// The size of this type in bits.
pub fn bits() -> u32 {
256
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
out
target
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[[package]]
name = 'core'
source = 'path+from-root-31E9CFD36AA5AC17'
dependencies = []

[[package]]
name = 'std'
source = 'path+from-root-31E9CFD36AA5AC17'
dependencies = ['core']

[[package]]
name = 'u256_test'
source = 'root'
dependencies = ['std']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "u256_test"

[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"inputs": [],
"name": "main",
"outputs": [
{
"components": null,
"name": "",
"type": "bool"
}
],
"type": "function"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
script;

use core::num::*;
use std::{assert::assert, result::Result, u256::{U256, U256Error}};

fn main() -> bool {
// test new()
let new = ~U256::new();
let empty = U256 {
a: 0,
b: 0,
c: 0,
d: 0,
};
assert(new == empty);

// test from() & into()
let(l, m, n, o) = new.into();
assert(l == 0);
assert(m == 0);
assert(n == 0);
assert(o == 0);

let a = 11;
let b = 42;
let c = 101;
let d = 69;
let x = ~U256::from(a, b, c, d);
let y = ~U256::from(a, b, c, d);

assert(x.a == a);
assert(x.a != b);
assert(x.b == b);
assert(x.b != c);
assert(x.c == c);
assert(x.c != d);
assert(x.d == d);
assert(x.d != a);

let(e, f, g, h) = x.into();
assert(e == a);
assert(f == b);
assert(g == c);
assert(h == d);

assert(x == y);

// test min() & max()
let max = ~U256::max();
let min = ~U256::min();
let(one, two, three, four) = max.into();
assert(one == ~u64::max());
assert(two == ~u64::max());
assert(three == ~u64::max());
assert(four == ~u64::max());

let(min_1, min_2, min_3, min_4) = min.into();
assert(min_1 == ~u64::min());
assert(min_2 == ~u64::min());
assert(min_3 == ~u64::min());
assert(min_4 == ~u64::min());

// test as_u64()
let err_1 = ~U256::from(42, 0, 0, 11).as_u64();
assert(match err_1 {
Result::Err(U256Error::LossOfPrecision) => {
true
},
_ => {
false
},
});

let err_2 = ~U256::from(0, 42, 0, 11).as_u64();
assert(match err_2 {
Result::Err(U256Error::LossOfPrecision) => {
true
},
_ => {
false
},
});

let err_3 = ~U256::from(0, 0, 42, 11).as_u64();
assert(match err_3 {
Result::Err(U256Error::LossOfPrecision) => {
true
},
_ => {
false
},
});


let eleven = ~U256::from(0, 0, 0, 11);
let unwrapped = eleven.as_u64().unwrap();
assert(unwrapped == 11);

// test bits()
assert(~U256::bits() == 256u32);

true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
category = "run"
expected_result = { action = "return", value = 1 }
validate_abi = true

0 comments on commit 1a64db9

Please sign in to comment.