Last active
August 9, 2020 19:28
-
-
Save skyfly200/46b622e8c362866022757d5ddde4caf5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: Unlicensed | |
pragma solidity ^0.6.8; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/utils/Strings.sol"; | |
import "./libraries/Utils.sol"; | |
import "./libraries/FixidityLib.sol"; | |
import "./libraries/SVGBuffer.sol"; | |
contract ColorTest { | |
using SVGBuffer for *; | |
using Strings for uint256; | |
using Utils for *; | |
/** | |
* @dev Contract constructor. | |
*/ | |
constructor() public { | |
} | |
// Test the Fixidity library | |
function test(int256 base, uint8 decimals, uint8 scheme) external view returns (string memory) { | |
// empty buffer for the SVG markup | |
bytes memory buffer = new bytes(8192); | |
int256 root = FixidityLib.newFixed(base, decimals); | |
uint8 printDecimals = 5; | |
buffer.append('Root Hue: '); | |
buffer.append(root.toString()); | |
uint16[1] memory schemes1 = [ | |
uint16(180) // complimentary | |
]; | |
uint16[2][1] memory schemes2 = [ | |
[uint16(30), uint16(330)], // analogous | |
[uint16(150), uint16(210)] // split complimentary | |
]; | |
uint16[3][5] memory schemes3 = [ | |
[uint16(150), uint16(180), uint16(210)], // complimentary and analogous | |
[uint16(30), uint16(330), uint16(180)], // analogous and complimentary | |
[uint16(30), uint16(330), uint16(180)], // split complimentary | |
[uint16(60), uint16(180), uint16(240)], // tetradic | |
[uint16(90), uint16(180), uint16(270)] // square | |
]; | |
for (uint256 i = 0; i < schemes3[scheme].length; i++) { | |
int256 rel = FixidityLib.newFixed(int256(schemes3[scheme][i])); | |
int256 result = FixidityLib.add(root, rel); | |
buffer.append('\nColor '); | |
buffer.append((i + 1).toString()); | |
buffer.append(': '); | |
buffer.append(FixidityLib.fromFixed(FixidityLib.integer(result)).toString()); | |
buffer.append('.'); | |
buffer.append(FixidityLib.fromFixed(FixidityLib.fractional(result), printDecimals).toString()); | |
} | |
return buffer.toString(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: Unlicensed | |
pragma solidity ^0.6.4; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/math/SafeMath.sol"; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/math/SignedSafeMath.sol"; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/utils/Strings.sol"; | |
import "../libraries/SVGBuffer.sol"; | |
import "../structs/DecimalStruct.sol"; | |
library DecimalUtils { | |
using SafeMath for uint256; | |
using SignedSafeMath for int256; | |
using SVGBuffer for bytes; | |
using Strings for *; | |
// compute decimal parts | |
function decimalParts(Decimal memory number) internal pure returns (int256, uint256) { | |
return ( | |
int256(number.value).div(int256(10) ** number.decimals), | |
uint256(number.value).mod(uint256(10) ** number.decimals) | |
); | |
} | |
// convert a Decimal to a string | |
// TODO: fix overflow error | |
function toString(Decimal memory number) internal view returns (string memory) { | |
( int256 wholeComponent, uint256 decimalComponent ) = decimalParts(number); | |
bytes memory buffer = new bytes(20); | |
if (wholeComponent < 0) { | |
buffer.append("-"); | |
buffer.append(uint256(wholeComponent.mul(-1)).toString()); | |
} else { | |
buffer.append(uint256(wholeComponent).toString()); | |
} | |
buffer.append("."); | |
buffer.append(decimalComponent.toString()); | |
return buffer.toString(); | |
} | |
// add two decimals | |
function add(Decimal memory a, Decimal memory b) internal pure returns (Decimal memory result) { | |
// decide the order to add by decimal length | |
(Decimal memory x, Decimal memory y) = a.decimals > b.decimals ? (a, b) : (b, a); | |
// scale less precise value to match larger decimals | |
int256 scaled = y.value.mul(int256(10)**(x.decimals - y.decimals)); | |
// add scaled values and return a new decimal | |
return Decimal(scaled.add(x.value), x.decimals); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: Unlicensed | |
pragma solidity ^0.6.4; | |
struct Decimal { | |
int256 value; | |
uint8 decimals; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: MIT | |
pragma solidity ^0.6.8; | |
/** | |
* @title FixidityLib | |
* @author Gadi Guy, Alberto Cuesta Canada | |
* @notice This library provides fixed point arithmetic with protection against | |
* overflow. | |
* All operations are done with int256 and the operands must have been created | |
* with any of the newFrom* functions, which shift the comma digits() to the | |
* right and check for limits. | |
* When using this library be sure of using maxNewFixed() as the upper limit for | |
* creation of fixed point numbers. Use maxFixedMul(), maxFixedDiv() and | |
* maxFixedAdd() if you want to be certain that those operations don't | |
* overflow. | |
*/ | |
library FixidityLib { | |
/** | |
* @notice Number of positions that the comma is shifted to the right. | |
*/ | |
function digits() public pure returns(uint8) { | |
return 24; | |
} | |
/** | |
* @notice This is 1 in the fixed point units used in this library. | |
* @dev Test fixed1() equals 10^digits() | |
* Hardcoded to 24 digits. | |
*/ | |
function fixed1() public pure returns(int256) { | |
return 1000000000000000000000000; | |
} | |
/** | |
* @notice The amount of decimals lost on each multiplication operand. | |
* @dev Test mulPrecision() equals sqrt(fixed1) | |
* Hardcoded to 24 digits. | |
*/ | |
function mulPrecision() public pure returns(int256) { | |
return 1000000000000; | |
} | |
/** | |
* @notice Maximum value that can be represented in an int256 | |
* @dev Test maxInt256() equals 2^255 -1 | |
*/ | |
function maxInt256() public pure returns(int256) { | |
return 57896044618658097711785492504343953926634992332820282019728792003956564819967; | |
} | |
/** | |
* @notice Minimum value that can be represented in an int256 | |
* @dev Test minInt256 equals (2^255) * (-1) | |
*/ | |
function minInt256() public pure returns(int256) { | |
return -57896044618658097711785492504343953926634992332820282019728792003956564819968; | |
} | |
/** | |
* @notice Maximum value that can be converted to fixed point. Optimize for | |
* @dev deployment. | |
* Test maxNewFixed() equals maxInt256() / fixed1() | |
* Hardcoded to 24 digits. | |
*/ | |
function maxNewFixed() public pure returns(int256) { | |
return 57896044618658097711785492504343953926634992332820282; | |
} | |
/** | |
* @notice Maximum value that can be converted to fixed point. Optimize for | |
* deployment. | |
* @dev Test minNewFixed() equals -(maxInt256()) / fixed1() | |
* Hardcoded to 24 digits. | |
*/ | |
function minNewFixed() public pure returns(int256) { | |
return -57896044618658097711785492504343953926634992332820282; | |
} | |
/** | |
* @notice Maximum value that can be safely used as an addition operator. | |
* @dev Test maxFixedAdd() equals maxInt256()-1 / 2 | |
* Test add(maxFixedAdd(),maxFixedAdd()) equals maxFixedAdd() + maxFixedAdd() | |
* Test add(maxFixedAdd()+1,maxFixedAdd()) throws | |
* Test add(-maxFixedAdd(),-maxFixedAdd()) equals -maxFixedAdd() - maxFixedAdd() | |
* Test add(-maxFixedAdd(),-maxFixedAdd()-1) throws | |
*/ | |
function maxFixedAdd() public pure returns(int256) { | |
return 28948022309329048855892746252171976963317496166410141009864396001978282409983; | |
} | |
/** | |
* @notice Maximum negative value that can be safely in a subtraction. | |
* @dev Test maxFixedSub() equals minInt256() / 2 | |
*/ | |
function maxFixedSub() public pure returns(int256) { | |
return -28948022309329048855892746252171976963317496166410141009864396001978282409984; | |
} | |
/** | |
* @notice Maximum value that can be safely used as a multiplication operator. | |
* @dev Calculated as sqrt(maxInt256()*fixed1()). | |
* Be careful with your sqrt() implementation. I couldn't find a calculator | |
* that would give the exact square root of maxInt256*fixed1 so this number | |
* is below the real number by no more than 3*10**28. It is safe to use as | |
* a limit for your multiplications, although powers of two of numbers over | |
* this value might still work. | |
* Test multiply(maxFixedMul(),maxFixedMul()) equals maxFixedMul() * maxFixedMul() | |
* Test multiply(maxFixedMul(),maxFixedMul()+1) throws | |
* Test multiply(-maxFixedMul(),maxFixedMul()) equals -maxFixedMul() * maxFixedMul() | |
* Test multiply(-maxFixedMul(),maxFixedMul()+1) throws | |
* Hardcoded to 24 digits. | |
*/ | |
function maxFixedMul() public pure returns(int256) { | |
return 240615969168004498257251713877715648331380787511296; | |
} | |
/** | |
* @notice Maximum value that can be safely used as a dividend. | |
* @dev divide(maxFixedDiv,newFixedFraction(1,fixed1())) = maxInt256(). | |
* Test maxFixedDiv() equals maxInt256()/fixed1() | |
* Test divide(maxFixedDiv(),multiply(mulPrecision(),mulPrecision())) = maxFixedDiv()*(10^digits()) | |
* Test divide(maxFixedDiv()+1,multiply(mulPrecision(),mulPrecision())) throws | |
* Hardcoded to 24 digits. | |
*/ | |
function maxFixedDiv() public pure returns(int256) { | |
return 57896044618658097711785492504343953926634992332820282; | |
} | |
/** | |
* @notice Maximum value that can be safely used as a divisor. | |
* @dev Test maxFixedDivisor() equals fixed1()*fixed1() - Or 10**(digits()*2) | |
* Test divide(10**(digits()*2 + 1),10**(digits()*2)) = returns 10*fixed1() | |
* Test divide(10**(digits()*2 + 1),10**(digits()*2 + 1)) = throws | |
* Hardcoded to 24 digits. | |
*/ | |
function maxFixedDivisor() public pure returns(int256) { | |
return 1000000000000000000000000000000000000000000000000; | |
} | |
/** | |
* @notice Converts an int256 to fixed point units, equivalent to multiplying | |
* by 10^digits(). | |
* @dev Test newFixed(0) returns 0 | |
* Test newFixed(1) returns fixed1() | |
* Test newFixed(maxNewFixed()) returns maxNewFixed() * fixed1() | |
* Test newFixed(maxNewFixed()+1) fails | |
*/ | |
function newFixed(int256 x) | |
public | |
pure | |
returns (int256) | |
{ | |
assert(x <= maxNewFixed()); | |
assert(x >= minNewFixed()); | |
return x * fixed1(); | |
} | |
/** | |
* @notice Converts an int256 in the fixed point representation of this | |
* library to a non decimal. All decimal digits will be truncated. | |
*/ | |
function fromFixed(int256 x) | |
public | |
pure | |
returns (int256) | |
{ | |
return x / fixed1(); | |
} | |
/** | |
* @notice Converts an int256 which is already in some fixed point | |
* representation to a different fixed precision representation. | |
* Both the origin and destination precisions must be 38 or less digits. | |
* Origin values with a precision higher than the destination precision | |
* will be truncated accordingly. | |
* @dev | |
* Test convertFixed(1,0,0) returns 1; | |
* Test convertFixed(1,1,1) returns 1; | |
* Test convertFixed(1,1,0) returns 0; | |
* Test convertFixed(1,0,1) returns 10; | |
* Test convertFixed(10,1,0) returns 1; | |
* Test convertFixed(10,0,1) returns 100; | |
* Test convertFixed(100,1,0) returns 10; | |
* Test convertFixed(100,0,1) returns 1000; | |
* Test convertFixed(1000,2,0) returns 10; | |
* Test convertFixed(1000,0,2) returns 100000; | |
* Test convertFixed(1000,2,1) returns 100; | |
* Test convertFixed(1000,1,2) returns 10000; | |
* Test convertFixed(maxInt256,1,0) returns maxInt256/10; | |
* Test convertFixed(maxInt256,0,1) throws | |
* Test convertFixed(maxInt256,38,0) returns maxInt256/(10**38); | |
* Test convertFixed(1,0,38) returns 10**38; | |
* Test convertFixed(maxInt256,39,0) throws | |
* Test convertFixed(1,0,39) throws | |
*/ | |
function convertFixed(int256 x, uint8 _originDigits, uint8 _destinationDigits) | |
public | |
pure | |
returns (int256) | |
{ | |
assert(_originDigits <= 38 && _destinationDigits <= 38); | |
uint8 decimalDifference; | |
if ( _originDigits > _destinationDigits ){ | |
decimalDifference = _originDigits - _destinationDigits; | |
return x/(uint128(10)**uint128(decimalDifference)); | |
} | |
else if ( _originDigits < _destinationDigits ){ | |
decimalDifference = _destinationDigits - _originDigits; | |
// Cast uint8 -> uint128 is safe | |
// Exponentiation is safe: | |
// _originDigits and _destinationDigits limited to 38 or less | |
// decimalDifference = abs(_destinationDigits - _originDigits) | |
// decimalDifference < 38 | |
// 10**38 < 2**128-1 | |
assert(x <= maxInt256()/uint128(10)**uint128(decimalDifference)); | |
assert(x >= minInt256()/uint128(10)**uint128(decimalDifference)); | |
return x*(uint128(10)**uint128(decimalDifference)); | |
} | |
// _originDigits == digits()) | |
return x; | |
} | |
/** | |
* @notice Converts an int256 which is already in some fixed point | |
* representation to that of this library. The _originDigits parameter is the | |
* precision of x. Values with a precision higher than FixidityLib.digits() | |
* will be truncated accordingly. | |
*/ | |
function newFixed(int256 x, uint8 _originDigits) | |
public | |
pure | |
returns (int256) | |
{ | |
return convertFixed(x, _originDigits, digits()); | |
} | |
/** | |
* @notice Converts an int256 in the fixed point representation of this | |
* library to a different representation. The _destinationDigits parameter is the | |
* precision of the output x. Values with a precision below than | |
* FixidityLib.digits() will be truncated accordingly. | |
*/ | |
function fromFixed(int256 x, uint8 _destinationDigits) | |
public | |
pure | |
returns (int256) | |
{ | |
return convertFixed(x, digits(), _destinationDigits); | |
} | |
/** | |
* @notice Converts two int256 representing a fraction to fixed point units, | |
* equivalent to multiplying dividend and divisor by 10^digits(). | |
* @dev | |
* Test newFixedFraction(maxFixedDiv()+1,1) fails | |
* Test newFixedFraction(1,maxFixedDiv()+1) fails | |
* Test newFixedFraction(1,0) fails | |
* Test newFixedFraction(0,1) returns 0 | |
* Test newFixedFraction(1,1) returns fixed1() | |
* Test newFixedFraction(maxFixedDiv(),1) returns maxFixedDiv()*fixed1() | |
* Test newFixedFraction(1,fixed1()) returns 1 | |
* Test newFixedFraction(1,fixed1()-1) returns 0 | |
*/ | |
function newFixedFraction( | |
int256 numerator, | |
int256 denominator | |
) | |
public | |
pure | |
returns (int256) | |
{ | |
assert(numerator <= maxNewFixed()); | |
assert(denominator <= maxNewFixed()); | |
assert(denominator != 0); | |
int256 convertedNumerator = newFixed(numerator); | |
int256 convertedDenominator = newFixed(denominator); | |
return divide(convertedNumerator, convertedDenominator); | |
} | |
/** | |
* @notice Returns the integer part of a fixed point number. | |
* @dev | |
* Test integer(0) returns 0 | |
* Test integer(fixed1()) returns fixed1() | |
* Test integer(newFixed(maxNewFixed())) returns maxNewFixed()*fixed1() | |
* Test integer(-fixed1()) returns -fixed1() | |
* Test integer(newFixed(-maxNewFixed())) returns -maxNewFixed()*fixed1() | |
*/ | |
function integer(int256 x) public pure returns (int256) { | |
return (x / fixed1()) * fixed1(); // Can't overflow | |
} | |
/** | |
* @notice Returns the fractional part of a fixed point number. | |
* In the case of a negative number the fractional is also negative. | |
* @dev | |
* Test fractional(0) returns 0 | |
* Test fractional(fixed1()) returns 0 | |
* Test fractional(fixed1()-1) returns 10^24-1 | |
* Test fractional(-fixed1()) returns 0 | |
* Test fractional(-fixed1()+1) returns -10^24-1 | |
*/ | |
function fractional(int256 x) public pure returns (int256) { | |
return x - (x / fixed1()) * fixed1(); // Can't overflow | |
} | |
/** | |
* @notice Converts to positive if negative. | |
* Due to int256 having one more negative number than positive numbers | |
* abs(minInt256) reverts. | |
* @dev | |
* Test abs(0) returns 0 | |
* Test abs(fixed1()) returns -fixed1() | |
* Test abs(-fixed1()) returns fixed1() | |
* Test abs(newFixed(maxNewFixed())) returns maxNewFixed()*fixed1() | |
* Test abs(newFixed(minNewFixed())) returns -minNewFixed()*fixed1() | |
*/ | |
function abs(int256 x) public pure returns (int256) { | |
if (x >= 0) { | |
return x; | |
} else { | |
int256 result = -x; | |
assert (result > 0); | |
return result; | |
} | |
} | |
/** | |
* @notice x+y. If any operator is higher than maxFixedAdd() it | |
* might overflow. | |
* In solidity maxInt256 + 1 = minInt256 and viceversa. | |
* @dev | |
* Test add(maxFixedAdd(),maxFixedAdd()) returns maxInt256()-1 | |
* Test add(maxFixedAdd()+1,maxFixedAdd()+1) fails | |
* Test add(-maxFixedSub(),-maxFixedSub()) returns minInt256() | |
* Test add(-maxFixedSub()-1,-maxFixedSub()-1) fails | |
* Test add(maxInt256(),maxInt256()) fails | |
* Test add(minInt256(),minInt256()) fails | |
*/ | |
function add(int256 x, int256 y) public pure returns (int256) { | |
int256 z = x + y; | |
if (x > 0 && y > 0) assert(z > x && z > y); | |
if (x < 0 && y < 0) assert(z < x && z < y); | |
return z; | |
} | |
/** | |
* @notice x-y. You can use add(x,-y) instead. | |
* @dev Tests covered by add(x,y) | |
*/ | |
function subtract(int256 x, int256 y) public pure returns (int256) { | |
return add(x,-y); | |
} | |
/** | |
* @notice x*y. If any of the operators is higher than maxFixedMul() it | |
* might overflow. | |
* @dev | |
* Test multiply(0,0) returns 0 | |
* Test multiply(maxFixedMul(),0) returns 0 | |
* Test multiply(0,maxFixedMul()) returns 0 | |
* Test multiply(maxFixedMul(),fixed1()) returns maxFixedMul() | |
* Test multiply(fixed1(),maxFixedMul()) returns maxFixedMul() | |
* Test all combinations of (2,-2), (2, 2.5), (2, -2.5) and (0.5, -0.5) | |
* Test multiply(fixed1()/mulPrecision(),fixed1()*mulPrecision()) | |
* Test multiply(maxFixedMul()-1,maxFixedMul()) equals multiply(maxFixedMul(),maxFixedMul()-1) | |
* Test multiply(maxFixedMul(),maxFixedMul()) returns maxInt256() // Probably not to the last digits | |
* Test multiply(maxFixedMul()+1,maxFixedMul()) fails | |
* Test multiply(maxFixedMul(),maxFixedMul()+1) fails | |
*/ | |
function multiply(int256 x, int256 y) public pure returns (int256) { | |
if (x == 0 || y == 0) return 0; | |
if (y == fixed1()) return x; | |
if (x == fixed1()) return y; | |
// Separate into integer and fractional parts | |
// x = x1 + x2, y = y1 + y2 | |
int256 x1 = integer(x) / fixed1(); | |
int256 x2 = fractional(x); | |
int256 y1 = integer(y) / fixed1(); | |
int256 y2 = fractional(y); | |
// (x1 + x2) * (y1 + y2) = (x1 * y1) + (x1 * y2) + (x2 * y1) + (x2 * y2) | |
int256 x1y1 = x1 * y1; | |
if (x1 != 0) assert(x1y1 / x1 == y1); // Overflow x1y1 | |
// x1y1 needs to be multiplied back by fixed1 | |
// solium-disable-next-line mixedcase | |
int256 fixed_x1y1 = x1y1 * fixed1(); | |
if (x1y1 != 0) assert(fixed_x1y1 / x1y1 == fixed1()); // Overflow x1y1 * fixed1 | |
x1y1 = fixed_x1y1; | |
int256 x2y1 = x2 * y1; | |
if (x2 != 0) assert(x2y1 / x2 == y1); // Overflow x2y1 | |
int256 x1y2 = x1 * y2; | |
if (x1 != 0) assert(x1y2 / x1 == y2); // Overflow x1y2 | |
x2 = x2 / mulPrecision(); | |
y2 = y2 / mulPrecision(); | |
int256 x2y2 = x2 * y2; | |
if (x2 != 0) assert(x2y2 / x2 == y2); // Overflow x2y2 | |
// result = fixed1() * x1 * y1 + x1 * y2 + x2 * y1 + x2 * y2 / fixed1(); | |
int256 result = x1y1; | |
result = add(result, x2y1); // Add checks for overflow | |
result = add(result, x1y2); // Add checks for overflow | |
result = add(result, x2y2); // Add checks for overflow | |
return result; | |
} | |
/** | |
* @notice 1/x | |
* @dev | |
* Test reciprocal(0) fails | |
* Test reciprocal(fixed1()) returns fixed1() | |
* Test reciprocal(fixed1()*fixed1()) returns 1 // Testing how the fractional is truncated | |
* Test reciprocal(2*fixed1()*fixed1()) returns 0 // Testing how the fractional is truncated | |
*/ | |
function reciprocal(int256 x) public pure returns (int256) { | |
assert(x != 0); | |
return (fixed1()*fixed1()) / x; // Can't overflow | |
} | |
/** | |
* @notice x/y. If the dividend is higher than maxFixedDiv() it | |
* might overflow. You can use multiply(x,reciprocal(y)) instead. | |
* There is a loss of precision on division for the lower mulPrecision() decimals. | |
* @dev | |
* Test divide(fixed1(),0) fails | |
* Test divide(maxFixedDiv(),1) = maxFixedDiv()*(10^digits()) | |
* Test divide(maxFixedDiv()+1,1) throws | |
* Test divide(maxFixedDiv(),maxFixedDiv()) returns fixed1() | |
*/ | |
function divide(int256 x, int256 y) public pure returns (int256) { | |
if (y == fixed1()) return x; | |
assert(y != 0); | |
assert(y <= maxFixedDivisor()); | |
return multiply(x, reciprocal(y)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: Unlicensed | |
pragma solidity ^0.6.8; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/utils/Strings.sol"; | |
import "../structs/DecimalStruct.sol"; | |
import "./Decimal.sol"; | |
library SVGBuffer { | |
using DecimalUtils for Decimal; | |
using Strings for *; | |
function hasCapacityFor(bytes memory buffer, uint256 needed) | |
internal | |
pure | |
returns (bool) | |
{ | |
uint256 size; | |
uint256 used; | |
assembly { | |
size := mload(buffer) | |
used := mload(add(buffer, 32)) | |
} | |
return size >= 32 && used <= size - 32 && used + needed <= size - 32; | |
} | |
function toString(bytes memory buffer) | |
internal | |
pure | |
returns (string memory) | |
{ | |
require(hasCapacityFor(buffer, 0), "Buffer.toString: invalid buffer"); | |
string memory ret; | |
assembly { | |
ret := add(buffer, 32) | |
} | |
return ret; | |
} | |
function append(bytes memory buffer, string memory str) internal view { | |
require( | |
hasCapacityFor(buffer, bytes(str).length), | |
"Buffer.append: no capacity" | |
); | |
assembly { | |
let len := mload(add(buffer, 32)) | |
pop( | |
staticcall( | |
gas(), | |
0x4, | |
add(str, 32), | |
mload(str), | |
add(len, add(buffer, 64)), | |
mload(str) | |
) | |
) | |
mstore(add(buffer, 32), add(len, mload(str))) | |
} | |
} | |
function rect( | |
bytes memory buffer, | |
Decimal[2] memory positions, | |
Decimal[2] memory size, | |
Decimal memory opacity, | |
uint256 _radius, | |
uint256 rgb | |
) internal view { | |
require(hasCapacityFor(buffer, 102), "Buffer.rect: no capacity"); | |
string memory xpos = positions[0].toString(); | |
string memory ypos = positions[1].toString(); | |
string memory width = size[0].toString(); | |
string memory height = size[1].toString(); | |
string memory radius = _radius.toString(); | |
string memory opacityString = opacity.toString(); | |
assembly { | |
function numbx1(x, v) -> y { | |
// v must be in the closed interval [0, 9] | |
// otherwise it outputs junk | |
mstore8(x, add(v, 48)) | |
y := add(x, 1) | |
} | |
function numbx2(x, v) -> y { | |
// v must be in the closed interval [0, 99] | |
// otherwise it outputs junk | |
y := numbx1(numbx1(x, div(v, 10)), mod(v, 10)) | |
} | |
function numbu3(x, v) -> y { | |
// v must be in the closed interval [0, 999] | |
// otherwise only the last 3 digits will be converted | |
switch lt(v, 100) | |
case 0 { | |
// without input value sanitation: y := numbx2(numbx1(x, div(v, 100)), mod(v, 100)) | |
y := numbx2( | |
numbx1(x, mod(div(v, 100), 10)), | |
mod(v, 100) | |
) | |
} | |
default { | |
switch lt(v, 10) | |
case 0 { | |
y := numbx2(x, v) | |
} | |
default { | |
y := numbx1(x, v) | |
} | |
} | |
} | |
function numbi3(x, v) -> y { | |
// v must be in the closed interval [-999, 999] | |
// otherwise only the last 3 digits will be converted | |
if slt(v, 0) { | |
v := add(not(v), 1) | |
mstore8(x, 45) // minus sign | |
x := add(x, 1) | |
} | |
y := numbu3(x, v) | |
} | |
function hexrgb(x, v) -> y { | |
let blo := and(v, 0xf) | |
let bhi := and(shr(4, v), 0xf) | |
let glo := and(shr(8, v), 0xf) | |
let ghi := and(shr(12, v), 0xf) | |
let rlo := and(shr(16, v), 0xf) | |
let rhi := and(shr(20, v), 0xf) | |
mstore8(x, add(add(rhi, mul(div(rhi, 10), 39)), 48)) | |
mstore8(add(x, 1), add(add(rlo, mul(div(rlo, 10), 39)), 48)) | |
mstore8(add(x, 2), add(add(ghi, mul(div(ghi, 10), 39)), 48)) | |
mstore8(add(x, 3), add(add(glo, mul(div(glo, 10), 39)), 48)) | |
mstore8(add(x, 4), add(add(bhi, mul(div(bhi, 10), 39)), 48)) | |
mstore8(add(x, 5), add(add(blo, mul(div(blo, 10), 39)), 48)) | |
y := add(x, 6) | |
} | |
function append(x, str, len) -> y { | |
mstore(x, str) | |
y := add(x, len) | |
} | |
let strIdx := add(mload(add(buffer, 32)), add(buffer, 64)) | |
strIdx := append(strIdx, '<rect x="', 9) | |
strIdx := append(strIdx, xpos, 3) | |
strIdx := append(strIdx, '" y="', 5) | |
strIdx := append(strIdx, ypos, 3) | |
strIdx := append(strIdx, '" width="', 9) | |
strIdx := append(strIdx, width, 3) | |
strIdx := append(strIdx, '" height="', 10) | |
strIdx := append(strIdx, height, 3) | |
strIdx := append(strIdx, '" rx="', 6) | |
strIdx := append(strIdx, radius, 3) | |
strIdx := append(strIdx, '" style="fill:#', 15) | |
strIdx := hexrgb(strIdx, rgb) | |
strIdx := append(strIdx, "; fill-opacity:", 14) | |
strIdx := append(strIdx, opacityString, 7) | |
strIdx := append(strIdx, '"/>', 3) | |
mstore(add(buffer, 32), sub(sub(strIdx, buffer), 64)) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: Unlicensed | |
pragma solidity ^0.6.4; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/math/SignedSafeMath.sol"; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/utils/Strings.sol"; | |
import "./SVGBuffer.sol"; | |
library Utils { | |
using SVGBuffer for bytes; | |
using SignedSafeMath for int256; | |
using Strings for uint256; | |
function stringToUint(string memory s) | |
internal | |
pure | |
returns (uint256 result) | |
{ | |
bytes memory b = bytes(s); | |
uint256 i; | |
result = 0; | |
for (i = 0; i < b.length; i++) { | |
uint256 c = uint256(uint8(b[i])); | |
if (c >= 48 && c <= 57) { | |
result = result * 10 + (c - 48); | |
} | |
} | |
} | |
// special toString for signed ints | |
function toString(int256 val) public view returns (string memory) { | |
bytes memory buffer = new bytes(8192); | |
if (val >= 0) buffer.append(uint256(val).toString()); | |
else { | |
buffer.append("-"); | |
buffer.append(uint256(int256(val).mul(-1)).toString()); | |
} | |
return buffer.toString(); | |
} | |
// special toString for signed 16 bit ints | |
function toString(int16 val) public view returns (string memory) { | |
bytes memory buffer = new bytes(8192); | |
if (val >= 0) buffer.append(uint256(val).toString()); | |
else { | |
buffer.append("-"); | |
buffer.append(uint256(int256(val).mul(-1)).toString()); | |
} | |
return buffer.toString(); | |
} | |
// TODO: fix overflow error | |
function toHexColor( | |
bytes memory buffer, | |
uint256 rgb | |
) internal pure { | |
require(SVGBuffer.hasCapacityFor(buffer, 6), "Buffer.rect: no capacity for color"); | |
assembly { | |
function hexrgb(x, v) -> y { | |
let blo := and(v, 0xf) | |
let bhi := and(shr(4, v), 0xf) | |
let glo := and(shr(8, v), 0xf) | |
let ghi := and(shr(12, v), 0xf) | |
let rlo := and(shr(16, v), 0xf) | |
let rhi := and(shr(20, v), 0xf) | |
mstore8(x, add(add(rhi, mul(div(rhi, 10), 39)), 48)) | |
mstore8(add(x, 1), add(add(rlo, mul(div(rlo, 10), 39)), 48)) | |
mstore8(add(x, 2), add(add(ghi, mul(div(ghi, 10), 39)), 48)) | |
mstore8(add(x, 3), add(add(glo, mul(div(glo, 10), 39)), 48)) | |
mstore8(add(x, 4), add(add(bhi, mul(div(bhi, 10), 39)), 48)) | |
mstore8(add(x, 5), add(add(blo, mul(div(blo, 10), 39)), 48)) | |
y := add(x, 6) | |
} | |
let strIdx := add(mload(add(buffer, 32)), add(buffer, 64)) | |
strIdx := hexrgb(strIdx, rgb) | |
mstore(add(buffer, 32), sub(sub(strIdx, buffer), 64)) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment