Browse Source

Add bytes6 to the Packing library. (#5077)

Hadrien Croubois 1 year ago
parent
commit
53b5d84212

+ 170 - 0
contracts/utils/Packing.sol

@@ -45,6 +45,24 @@ library Packing {
         }
     }
 
+    function pack_2_4(bytes2 left, bytes4 right) internal pure returns (bytes6 result) {
+        assembly ("memory-safe") {
+            result := or(left, shr(16, right))
+        }
+    }
+
+    function pack_2_6(bytes2 left, bytes6 right) internal pure returns (bytes8 result) {
+        assembly ("memory-safe") {
+            result := or(left, shr(16, right))
+        }
+    }
+
+    function pack_4_2(bytes4 left, bytes2 right) internal pure returns (bytes6 result) {
+        assembly ("memory-safe") {
+            result := or(left, shr(32, right))
+        }
+    }
+
     function pack_4_4(bytes4 left, bytes4 right) internal pure returns (bytes8 result) {
         assembly ("memory-safe") {
             result := or(left, shr(32, right))
@@ -87,6 +105,18 @@ library Packing {
         }
     }
 
+    function pack_6_2(bytes6 left, bytes2 right) internal pure returns (bytes8 result) {
+        assembly ("memory-safe") {
+            result := or(left, shr(48, right))
+        }
+    }
+
+    function pack_6_6(bytes6 left, bytes6 right) internal pure returns (bytes12 result) {
+        assembly ("memory-safe") {
+            result := or(left, shr(48, right))
+        }
+    }
+
     function pack_8_4(bytes8 left, bytes4 right) internal pure returns (bytes12 result) {
         assembly ("memory-safe") {
             result := or(left, shr(64, right))
@@ -255,6 +285,48 @@ library Packing {
         }
     }
 
+    function extract_6_1(bytes6 self, uint8 offset) internal pure returns (bytes1 result) {
+        if (offset > 5) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(248, not(0)))
+        }
+    }
+
+    function replace_6_1(bytes6 self, bytes1 value, uint8 offset) internal pure returns (bytes6 result) {
+        bytes1 oldValue = extract_6_1(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
+    function extract_6_2(bytes6 self, uint8 offset) internal pure returns (bytes2 result) {
+        if (offset > 4) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(240, not(0)))
+        }
+    }
+
+    function replace_6_2(bytes6 self, bytes2 value, uint8 offset) internal pure returns (bytes6 result) {
+        bytes2 oldValue = extract_6_2(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
+    function extract_6_4(bytes6 self, uint8 offset) internal pure returns (bytes4 result) {
+        if (offset > 2) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(224, not(0)))
+        }
+    }
+
+    function replace_6_4(bytes6 self, bytes4 value, uint8 offset) internal pure returns (bytes6 result) {
+        bytes4 oldValue = extract_6_4(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
     function extract_8_1(bytes8 self, uint8 offset) internal pure returns (bytes1 result) {
         if (offset > 7) revert OutOfRangeAccess();
         assembly ("memory-safe") {
@@ -297,6 +369,20 @@ library Packing {
         }
     }
 
+    function extract_8_6(bytes8 self, uint8 offset) internal pure returns (bytes6 result) {
+        if (offset > 2) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(208, not(0)))
+        }
+    }
+
+    function replace_8_6(bytes8 self, bytes6 value, uint8 offset) internal pure returns (bytes8 result) {
+        bytes6 oldValue = extract_8_6(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
     function extract_12_1(bytes12 self, uint8 offset) internal pure returns (bytes1 result) {
         if (offset > 11) revert OutOfRangeAccess();
         assembly ("memory-safe") {
@@ -339,6 +425,20 @@ library Packing {
         }
     }
 
+    function extract_12_6(bytes12 self, uint8 offset) internal pure returns (bytes6 result) {
+        if (offset > 6) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(208, not(0)))
+        }
+    }
+
+    function replace_12_6(bytes12 self, bytes6 value, uint8 offset) internal pure returns (bytes12 result) {
+        bytes6 oldValue = extract_12_6(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
     function extract_12_8(bytes12 self, uint8 offset) internal pure returns (bytes8 result) {
         if (offset > 4) revert OutOfRangeAccess();
         assembly ("memory-safe") {
@@ -395,6 +495,20 @@ library Packing {
         }
     }
 
+    function extract_16_6(bytes16 self, uint8 offset) internal pure returns (bytes6 result) {
+        if (offset > 10) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(208, not(0)))
+        }
+    }
+
+    function replace_16_6(bytes16 self, bytes6 value, uint8 offset) internal pure returns (bytes16 result) {
+        bytes6 oldValue = extract_16_6(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
     function extract_16_8(bytes16 self, uint8 offset) internal pure returns (bytes8 result) {
         if (offset > 8) revert OutOfRangeAccess();
         assembly ("memory-safe") {
@@ -465,6 +579,20 @@ library Packing {
         }
     }
 
+    function extract_20_6(bytes20 self, uint8 offset) internal pure returns (bytes6 result) {
+        if (offset > 14) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(208, not(0)))
+        }
+    }
+
+    function replace_20_6(bytes20 self, bytes6 value, uint8 offset) internal pure returns (bytes20 result) {
+        bytes6 oldValue = extract_20_6(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
     function extract_20_8(bytes20 self, uint8 offset) internal pure returns (bytes8 result) {
         if (offset > 12) revert OutOfRangeAccess();
         assembly ("memory-safe") {
@@ -549,6 +677,20 @@ library Packing {
         }
     }
 
+    function extract_24_6(bytes24 self, uint8 offset) internal pure returns (bytes6 result) {
+        if (offset > 18) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(208, not(0)))
+        }
+    }
+
+    function replace_24_6(bytes24 self, bytes6 value, uint8 offset) internal pure returns (bytes24 result) {
+        bytes6 oldValue = extract_24_6(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
     function extract_24_8(bytes24 self, uint8 offset) internal pure returns (bytes8 result) {
         if (offset > 16) revert OutOfRangeAccess();
         assembly ("memory-safe") {
@@ -647,6 +789,20 @@ library Packing {
         }
     }
 
+    function extract_28_6(bytes28 self, uint8 offset) internal pure returns (bytes6 result) {
+        if (offset > 22) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(208, not(0)))
+        }
+    }
+
+    function replace_28_6(bytes28 self, bytes6 value, uint8 offset) internal pure returns (bytes28 result) {
+        bytes6 oldValue = extract_28_6(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
     function extract_28_8(bytes28 self, uint8 offset) internal pure returns (bytes8 result) {
         if (offset > 20) revert OutOfRangeAccess();
         assembly ("memory-safe") {
@@ -759,6 +915,20 @@ library Packing {
         }
     }
 
+    function extract_32_6(bytes32 self, uint8 offset) internal pure returns (bytes6 result) {
+        if (offset > 26) revert OutOfRangeAccess();
+        assembly ("memory-safe") {
+            result := and(shl(mul(8, offset), self), shl(208, not(0)))
+        }
+    }
+
+    function replace_32_6(bytes32 self, bytes6 value, uint8 offset) internal pure returns (bytes32 result) {
+        bytes6 oldValue = extract_32_6(self, offset);
+        assembly ("memory-safe") {
+            result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
+        }
+    }
+
     function extract_32_8(bytes32 self, uint8 offset) internal pure returns (bytes8 result) {
         if (offset > 24) revert OutOfRangeAccess();
         assembly ("memory-safe") {

+ 22 - 14
scripts/generate/templates/Packing.js

@@ -36,43 +36,51 @@ pragma solidity ^0.8.20;
 `;
 
 const errors = `\
-    error OutOfRangeAccess();`;
+error OutOfRangeAccess();
+`;
 
-const pack = (left, right) => `
+const pack = (left, right) => `\
 function pack_${left}_${right}(bytes${left} left, bytes${right} right) internal pure returns (bytes${
   left + right
 } result) {
     assembly ("memory-safe") {
         result := or(left, shr(${8 * left}, right))
     }
-}`;
+}
+`;
 
-const extract = (outer, inner) => `
+const extract = (outer, inner) => `\
 function extract_${outer}_${inner}(bytes${outer} self, uint8 offset) internal pure returns (bytes${inner} result) {
     if (offset > ${outer - inner}) revert OutOfRangeAccess();
     assembly ("memory-safe") {
         result := and(shl(mul(8, offset), self), shl(${256 - 8 * inner}, not(0)))
     }
-}`;
+}
+`;
 
-const replace = (outer, inner) => `
+const replace = (outer, inner) => `\
 function replace_${outer}_${inner}(bytes${outer} self, bytes${inner} value, uint8 offset) internal pure returns (bytes${outer} result) {
     bytes${inner} oldValue = extract_${outer}_${inner}(self, offset);
     assembly ("memory-safe") {
         result := xor(self, shr(mul(8, offset), xor(oldValue, value)))
     }
-}`;
+}
+`;
 
 // GENERATE
 module.exports = format(
   header.trimEnd(),
   'library Packing {',
-  errors,
-  product(SIZES, SIZES)
-    .filter(([left, right]) => SIZES.includes(left + right))
-    .map(([left, right]) => pack(left, right)),
-  product(SIZES, SIZES)
-    .filter(([outer, inner]) => outer > inner)
-    .flatMap(([outer, inner]) => [extract(outer, inner), replace(outer, inner)]),
+  format(
+    [].concat(
+      errors,
+      product(SIZES, SIZES)
+        .filter(([left, right]) => SIZES.includes(left + right))
+        .map(([left, right]) => pack(left, right)),
+      product(SIZES, SIZES)
+        .filter(([outer, inner]) => outer > inner)
+        .flatMap(([outer, inner]) => [extract(outer, inner), replace(outer, inner)]),
+    ),
+  ).trimEnd(),
   '}',
 );

+ 3 - 5
scripts/generate/templates/Packing.opts.js

@@ -1,5 +1,3 @@
-const { range } = require('../../helpers');
-
-const SIZES = range(1, 33).filter(size => size == 1 || size == 2 || size % 4 == 0);
-
-module.exports = { SIZES };
+module.exports = {
+  SIZES: [1, 2, 4, 6, 8, 12, 16, 20, 24, 28, 32],
+};

+ 19 - 13
scripts/generate/templates/Packing.t.js

@@ -10,13 +10,14 @@ import {Test} from "forge-std/Test.sol";
 import {Packing} from "@openzeppelin/contracts/utils/Packing.sol";
 `;
 
-const testPack = (left, right) => `
+const testPack = (left, right) => `\
 function testPack(bytes${left} left, bytes${right} right) external {
     assertEq(left, Packing.pack_${left}_${right}(left, right).extract_${left + right}_${left}(0));
     assertEq(right, Packing.pack_${left}_${right}(left, right).extract_${left + right}_${right}(${left}));
-}`;
+}
+`;
 
-const testReplace = (outer, inner) => `
+const testReplace = (outer, inner) => `\
 function testReplace(bytes${outer} container, bytes${inner} newValue, uint8 offset) external {
     offset = uint8(bound(offset, 0, ${outer - inner}));
 
@@ -24,19 +25,24 @@ function testReplace(bytes${outer} container, bytes${inner} newValue, uint8 offs
 
     assertEq(newValue, container.replace_${outer}_${inner}(newValue, offset).extract_${outer}_${inner}(offset));
     assertEq(container, container.replace_${outer}_${inner}(newValue, offset).replace_${outer}_${inner}(oldValue, offset));
-}`;
+}
+`;
 
 // GENERATE
 module.exports = format(
-  header.trimEnd(),
-  '',
+  header,
   'contract PackingTest is Test {',
-  '    using Packing for *;',
-  product(SIZES, SIZES)
-    .filter(([left, right]) => SIZES.includes(left + right))
-    .map(([left, right]) => testPack(left, right)),
-  product(SIZES, SIZES)
-    .filter(([outer, inner]) => outer > inner)
-    .map(([outer, inner]) => testReplace(outer, inner)),
+  format(
+    [].concat(
+      'using Packing for *;',
+      '',
+      product(SIZES, SIZES)
+        .filter(([left, right]) => SIZES.includes(left + right))
+        .map(([left, right]) => testPack(left, right)),
+      product(SIZES, SIZES)
+        .filter(([outer, inner]) => outer > inner)
+        .map(([outer, inner]) => testReplace(outer, inner)),
+    ),
+  ).trimEnd(),
   '}',
 );

+ 115 - 0
test/utils/Packing.t.sol

@@ -19,6 +19,21 @@ contract PackingTest is Test {
         assertEq(right, Packing.pack_2_2(left, right).extract_4_2(2));
     }
 
+    function testPack(bytes2 left, bytes4 right) external {
+        assertEq(left, Packing.pack_2_4(left, right).extract_6_2(0));
+        assertEq(right, Packing.pack_2_4(left, right).extract_6_4(2));
+    }
+
+    function testPack(bytes2 left, bytes6 right) external {
+        assertEq(left, Packing.pack_2_6(left, right).extract_8_2(0));
+        assertEq(right, Packing.pack_2_6(left, right).extract_8_6(2));
+    }
+
+    function testPack(bytes4 left, bytes2 right) external {
+        assertEq(left, Packing.pack_4_2(left, right).extract_6_4(0));
+        assertEq(right, Packing.pack_4_2(left, right).extract_6_2(4));
+    }
+
     function testPack(bytes4 left, bytes4 right) external {
         assertEq(left, Packing.pack_4_4(left, right).extract_8_4(0));
         assertEq(right, Packing.pack_4_4(left, right).extract_8_4(4));
@@ -54,6 +69,16 @@ contract PackingTest is Test {
         assertEq(right, Packing.pack_4_28(left, right).extract_32_28(4));
     }
 
+    function testPack(bytes6 left, bytes2 right) external {
+        assertEq(left, Packing.pack_6_2(left, right).extract_8_6(0));
+        assertEq(right, Packing.pack_6_2(left, right).extract_8_2(6));
+    }
+
+    function testPack(bytes6 left, bytes6 right) external {
+        assertEq(left, Packing.pack_6_6(left, right).extract_12_6(0));
+        assertEq(right, Packing.pack_6_6(left, right).extract_12_6(6));
+    }
+
     function testPack(bytes8 left, bytes4 right) external {
         assertEq(left, Packing.pack_8_4(left, right).extract_12_8(0));
         assertEq(right, Packing.pack_8_4(left, right).extract_12_4(8));
@@ -186,6 +211,33 @@ contract PackingTest is Test {
         assertEq(container, container.replace_4_2(newValue, offset).replace_4_2(oldValue, offset));
     }
 
+    function testReplace(bytes6 container, bytes1 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 5));
+
+        bytes1 oldValue = container.extract_6_1(offset);
+
+        assertEq(newValue, container.replace_6_1(newValue, offset).extract_6_1(offset));
+        assertEq(container, container.replace_6_1(newValue, offset).replace_6_1(oldValue, offset));
+    }
+
+    function testReplace(bytes6 container, bytes2 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 4));
+
+        bytes2 oldValue = container.extract_6_2(offset);
+
+        assertEq(newValue, container.replace_6_2(newValue, offset).extract_6_2(offset));
+        assertEq(container, container.replace_6_2(newValue, offset).replace_6_2(oldValue, offset));
+    }
+
+    function testReplace(bytes6 container, bytes4 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 2));
+
+        bytes4 oldValue = container.extract_6_4(offset);
+
+        assertEq(newValue, container.replace_6_4(newValue, offset).extract_6_4(offset));
+        assertEq(container, container.replace_6_4(newValue, offset).replace_6_4(oldValue, offset));
+    }
+
     function testReplace(bytes8 container, bytes1 newValue, uint8 offset) external {
         offset = uint8(bound(offset, 0, 7));
 
@@ -213,6 +265,15 @@ contract PackingTest is Test {
         assertEq(container, container.replace_8_4(newValue, offset).replace_8_4(oldValue, offset));
     }
 
+    function testReplace(bytes8 container, bytes6 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 2));
+
+        bytes6 oldValue = container.extract_8_6(offset);
+
+        assertEq(newValue, container.replace_8_6(newValue, offset).extract_8_6(offset));
+        assertEq(container, container.replace_8_6(newValue, offset).replace_8_6(oldValue, offset));
+    }
+
     function testReplace(bytes12 container, bytes1 newValue, uint8 offset) external {
         offset = uint8(bound(offset, 0, 11));
 
@@ -240,6 +301,15 @@ contract PackingTest is Test {
         assertEq(container, container.replace_12_4(newValue, offset).replace_12_4(oldValue, offset));
     }
 
+    function testReplace(bytes12 container, bytes6 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 6));
+
+        bytes6 oldValue = container.extract_12_6(offset);
+
+        assertEq(newValue, container.replace_12_6(newValue, offset).extract_12_6(offset));
+        assertEq(container, container.replace_12_6(newValue, offset).replace_12_6(oldValue, offset));
+    }
+
     function testReplace(bytes12 container, bytes8 newValue, uint8 offset) external {
         offset = uint8(bound(offset, 0, 4));
 
@@ -276,6 +346,15 @@ contract PackingTest is Test {
         assertEq(container, container.replace_16_4(newValue, offset).replace_16_4(oldValue, offset));
     }
 
+    function testReplace(bytes16 container, bytes6 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 10));
+
+        bytes6 oldValue = container.extract_16_6(offset);
+
+        assertEq(newValue, container.replace_16_6(newValue, offset).extract_16_6(offset));
+        assertEq(container, container.replace_16_6(newValue, offset).replace_16_6(oldValue, offset));
+    }
+
     function testReplace(bytes16 container, bytes8 newValue, uint8 offset) external {
         offset = uint8(bound(offset, 0, 8));
 
@@ -321,6 +400,15 @@ contract PackingTest is Test {
         assertEq(container, container.replace_20_4(newValue, offset).replace_20_4(oldValue, offset));
     }
 
+    function testReplace(bytes20 container, bytes6 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 14));
+
+        bytes6 oldValue = container.extract_20_6(offset);
+
+        assertEq(newValue, container.replace_20_6(newValue, offset).extract_20_6(offset));
+        assertEq(container, container.replace_20_6(newValue, offset).replace_20_6(oldValue, offset));
+    }
+
     function testReplace(bytes20 container, bytes8 newValue, uint8 offset) external {
         offset = uint8(bound(offset, 0, 12));
 
@@ -375,6 +463,15 @@ contract PackingTest is Test {
         assertEq(container, container.replace_24_4(newValue, offset).replace_24_4(oldValue, offset));
     }
 
+    function testReplace(bytes24 container, bytes6 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 18));
+
+        bytes6 oldValue = container.extract_24_6(offset);
+
+        assertEq(newValue, container.replace_24_6(newValue, offset).extract_24_6(offset));
+        assertEq(container, container.replace_24_6(newValue, offset).replace_24_6(oldValue, offset));
+    }
+
     function testReplace(bytes24 container, bytes8 newValue, uint8 offset) external {
         offset = uint8(bound(offset, 0, 16));
 
@@ -438,6 +535,15 @@ contract PackingTest is Test {
         assertEq(container, container.replace_28_4(newValue, offset).replace_28_4(oldValue, offset));
     }
 
+    function testReplace(bytes28 container, bytes6 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 22));
+
+        bytes6 oldValue = container.extract_28_6(offset);
+
+        assertEq(newValue, container.replace_28_6(newValue, offset).extract_28_6(offset));
+        assertEq(container, container.replace_28_6(newValue, offset).replace_28_6(oldValue, offset));
+    }
+
     function testReplace(bytes28 container, bytes8 newValue, uint8 offset) external {
         offset = uint8(bound(offset, 0, 20));
 
@@ -510,6 +616,15 @@ contract PackingTest is Test {
         assertEq(container, container.replace_32_4(newValue, offset).replace_32_4(oldValue, offset));
     }
 
+    function testReplace(bytes32 container, bytes6 newValue, uint8 offset) external {
+        offset = uint8(bound(offset, 0, 26));
+
+        bytes6 oldValue = container.extract_32_6(offset);
+
+        assertEq(newValue, container.replace_32_6(newValue, offset).extract_32_6(offset));
+        assertEq(container, container.replace_32_6(newValue, offset).replace_32_6(oldValue, offset));
+    }
+
     function testReplace(bytes32 container, bytes8 newValue, uint8 offset) external {
         offset = uint8(bound(offset, 0, 24));