Merge LibMem and LibBytes
This commit is contained in:
@@ -33,7 +33,6 @@
|
||||
"TestAssetDataDecoders",
|
||||
"TestAssetProxyDispatcher",
|
||||
"TestLibBytes",
|
||||
"TestLibMem",
|
||||
"TestLibs",
|
||||
"TestSignatureValidator",
|
||||
"TestValidator",
|
||||
|
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"config": {
|
||||
"abis":
|
||||
"../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|ExchangeWrapper|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetDataDecoders|TestAssetProxyDispatcher|TestLibBytes|TestLibMem|TestLibs|TestSignatureValidator|TestValidator|TestWallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
|
||||
"../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|ExchangeWrapper|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetDataDecoders|TestAssetProxyDispatcher|TestLibBytes|TestLibs|TestSignatureValidator|TestValidator|TestWallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -221,4 +221,34 @@ contract TestLibBytes is
|
||||
writeBytes(b, index, input);
|
||||
return b;
|
||||
}
|
||||
|
||||
/// @dev Copies a block of memory from one location to another.
|
||||
/// @param mem Memory contents we want to apply memCopy to
|
||||
/// @param dest Destination offset into <mem>.
|
||||
/// @param source Source offset into <mem>.
|
||||
/// @param length Length of bytes to copy from <source> to <dest>
|
||||
/// @return mem Memory contents after calling memCopy.
|
||||
function testMemcpy(
|
||||
bytes mem,
|
||||
uint256 dest,
|
||||
uint256 source,
|
||||
uint256 length
|
||||
)
|
||||
public // not external, we need input in memory
|
||||
pure
|
||||
returns (bytes)
|
||||
{
|
||||
// Sanity check. Overflows are not checked.
|
||||
require(source + length <= mem.length);
|
||||
require(dest + length <= mem.length);
|
||||
|
||||
// Get pointer to memory contents
|
||||
uint256 offset = getMemAddress(mem) + 32;
|
||||
|
||||
// Execute memCopy adjusted for memory array location
|
||||
memCopy(offset + dest, offset + source, length);
|
||||
|
||||
// Return modified memory contents
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "../../utils/LibMem/LibMem.sol";
|
||||
|
||||
contract TestLibMem is
|
||||
LibMem
|
||||
{
|
||||
|
||||
/// @dev Copies a block of memory from one location to another.
|
||||
/// @param mem Memory contents we want to apply memCopy to
|
||||
/// @param dest Destination offset into <mem>.
|
||||
/// @param source Source offset into <mem>.
|
||||
/// @param length Length of bytes to copy from <source> to <dest>
|
||||
/// @return mem Memory contents after calling memCopy.
|
||||
function testMemcpy(
|
||||
bytes mem,
|
||||
uint256 dest,
|
||||
uint256 source,
|
||||
uint256 length
|
||||
)
|
||||
public // not external, we need input in memory
|
||||
pure
|
||||
returns (bytes)
|
||||
{
|
||||
// Sanity check. Overflows are not checked.
|
||||
require(source + length <= mem.length);
|
||||
require(dest + length <= mem.length);
|
||||
|
||||
// Get pointer to memory contents
|
||||
uint256 offset = getMemAddress(mem) + 32;
|
||||
|
||||
// Execute memCopy adjusted for memory array location
|
||||
memCopy(offset + dest, offset + source, length);
|
||||
|
||||
// Return modified memory contents
|
||||
return mem;
|
||||
}
|
||||
}
|
@@ -18,11 +18,7 @@
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
import "../LibMem/LibMem.sol";
|
||||
|
||||
contract LibBytes is
|
||||
LibMem
|
||||
{
|
||||
contract LibBytes {
|
||||
|
||||
// Revert reasons
|
||||
string constant GREATER_THAN_ZERO_LENGTH_REQUIRED = "GREATER_THAN_ZERO_LENGTH_REQUIRED";
|
||||
@@ -32,6 +28,125 @@ contract LibBytes is
|
||||
string constant GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_NESTED_BYTES_LENGTH_REQUIRED";
|
||||
string constant GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED = "GREATER_OR_EQUAL_TO_SOURCE_BYTES_LENGTH_REQUIRED";
|
||||
|
||||
/// @dev Gets the memory address for a byte array.
|
||||
/// @param input Byte array to lookup.
|
||||
/// @return memoryAddress Memory address of byte array.
|
||||
function getMemAddress(bytes memory input)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 memoryAddress)
|
||||
{
|
||||
assembly {
|
||||
memoryAddress := input
|
||||
}
|
||||
return memoryAddress;
|
||||
}
|
||||
|
||||
/// @dev Copies `length` bytes from memory location `source` to `dest`.
|
||||
/// @param dest memory address to copy bytes to.
|
||||
/// @param source memory address to copy bytes from.
|
||||
/// @param length number of bytes to copy.
|
||||
function memCopy(
|
||||
uint256 dest,
|
||||
uint256 source,
|
||||
uint256 length
|
||||
)
|
||||
internal
|
||||
pure
|
||||
{
|
||||
if (length < 32) {
|
||||
// Handle a partial word by reading destination and masking
|
||||
// off the bits we are interested in.
|
||||
// This correctly handles overlap, zero lengths and source == dest
|
||||
assembly {
|
||||
let mask := sub(exp(256, sub(32, length)), 1)
|
||||
let s := and(mload(source), not(mask))
|
||||
let d := and(mload(dest), mask)
|
||||
mstore(dest, or(s, d))
|
||||
}
|
||||
} else {
|
||||
// Skip the O(length) loop when source == dest.
|
||||
if (source == dest) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For large copies we copy whole words at a time. The final
|
||||
// word is aligned to the end of the range (instead of after the
|
||||
// previous) to handle partial words. So a copy will look like this:
|
||||
//
|
||||
// ####
|
||||
// ####
|
||||
// ####
|
||||
// ####
|
||||
//
|
||||
// We handle overlap in the source and destination range by
|
||||
// changing the copying direction. This prevents us from
|
||||
// overwriting parts of source that we still need to copy.
|
||||
//
|
||||
// This correctly handles source == dest
|
||||
//
|
||||
if (source > dest) {
|
||||
assembly {
|
||||
// We subtract 32 from `sEnd` and `dEnd` because it
|
||||
// is easier to compare with in the loop, and these
|
||||
// are also the addresses we need for copying the
|
||||
// last bytes.
|
||||
length := sub(length, 32)
|
||||
let sEnd := add(source, length)
|
||||
let dEnd := add(dest, length)
|
||||
|
||||
// Remember the last 32 bytes of source
|
||||
// This needs to be done here and not after the loop
|
||||
// because we may have overwritten the last bytes in
|
||||
// source already due to overlap.
|
||||
let last := mload(sEnd)
|
||||
|
||||
// Copy whole words front to back
|
||||
// Note: the first check is always true,
|
||||
// this could have been a do-while loop.
|
||||
for {} lt(source, sEnd) {} {
|
||||
mstore(dest, mload(source))
|
||||
source := add(source, 32)
|
||||
dest := add(dest, 32)
|
||||
}
|
||||
|
||||
// Write the last 32 bytes
|
||||
mstore(dEnd, last)
|
||||
}
|
||||
} else {
|
||||
assembly {
|
||||
// We subtract 32 from `sEnd` and `dEnd` because those
|
||||
// are the starting points when copying a word at the end.
|
||||
length := sub(length, 32)
|
||||
let sEnd := add(source, length)
|
||||
let dEnd := add(dest, length)
|
||||
|
||||
// Remember the first 32 bytes of source
|
||||
// This needs to be done here and not after the loop
|
||||
// because we may have overwritten the first bytes in
|
||||
// source already due to overlap.
|
||||
let first := mload(source)
|
||||
|
||||
// Copy whole words back to front
|
||||
// We use a signed comparisson here to allow dEnd to become
|
||||
// negative (happens when source and dest < 32). Valid
|
||||
// addresses in local memory will never be larger than
|
||||
// 2**255, so they can be safely re-interpreted as signed.
|
||||
// Note: the first check is always true,
|
||||
// this could have been a do-while loop.
|
||||
for {} slt(dest, dEnd) {} {
|
||||
mstore(dEnd, mload(sEnd))
|
||||
sEnd := sub(sEnd, 32)
|
||||
dEnd := sub(dEnd, 32)
|
||||
}
|
||||
|
||||
// Write the first 32 bytes
|
||||
mstore(dest, first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Pops the last byte off of a byte array by modifying its length.
|
||||
/// @param b Byte array that will be modified.
|
||||
/// @return The byte that was popped off.
|
||||
|
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.4.24;
|
||||
|
||||
contract LibMem
|
||||
{
|
||||
|
||||
/// @dev Gets the memory address for a byte array.
|
||||
/// @param input Byte array to lookup.
|
||||
/// @return memoryAddress Memory address of byte array.
|
||||
function getMemAddress(bytes memory input)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 memoryAddress)
|
||||
{
|
||||
assembly {
|
||||
memoryAddress := input
|
||||
}
|
||||
return memoryAddress;
|
||||
}
|
||||
|
||||
/// @dev Copies `length` bytes from memory location `source` to `dest`.
|
||||
/// @param dest memory address to copy bytes to.
|
||||
/// @param source memory address to copy bytes from.
|
||||
/// @param length number of bytes to copy.
|
||||
function memCopy(
|
||||
uint256 dest,
|
||||
uint256 source,
|
||||
uint256 length
|
||||
)
|
||||
internal
|
||||
pure
|
||||
{
|
||||
if (length < 32) {
|
||||
// Handle a partial word by reading destination and masking
|
||||
// off the bits we are interested in.
|
||||
// This correctly handles overlap, zero lengths and source == dest
|
||||
assembly {
|
||||
let mask := sub(exp(256, sub(32, length)), 1)
|
||||
let s := and(mload(source), not(mask))
|
||||
let d := and(mload(dest), mask)
|
||||
mstore(dest, or(s, d))
|
||||
}
|
||||
} else {
|
||||
// Skip the O(length) loop when source == dest.
|
||||
if (source == dest) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For large copies we copy whole words at a time. The final
|
||||
// word is aligned to the end of the range (instead of after the
|
||||
// previous) to handle partial words. So a copy will look like this:
|
||||
//
|
||||
// ####
|
||||
// ####
|
||||
// ####
|
||||
// ####
|
||||
//
|
||||
// We handle overlap in the source and destination range by
|
||||
// changing the copying direction. This prevents us from
|
||||
// overwriting parts of source that we still need to copy.
|
||||
//
|
||||
// This correctly handles source == dest
|
||||
//
|
||||
if (source > dest) {
|
||||
assembly {
|
||||
// We subtract 32 from `sEnd` and `dEnd` because it
|
||||
// is easier to compare with in the loop, and these
|
||||
// are also the addresses we need for copying the
|
||||
// last bytes.
|
||||
length := sub(length, 32)
|
||||
let sEnd := add(source, length)
|
||||
let dEnd := add(dest, length)
|
||||
|
||||
// Remember the last 32 bytes of source
|
||||
// This needs to be done here and not after the loop
|
||||
// because we may have overwritten the last bytes in
|
||||
// source already due to overlap.
|
||||
let last := mload(sEnd)
|
||||
|
||||
// Copy whole words front to back
|
||||
// Note: the first check is always true,
|
||||
// this could have been a do-while loop.
|
||||
for {} lt(source, sEnd) {} {
|
||||
mstore(dest, mload(source))
|
||||
source := add(source, 32)
|
||||
dest := add(dest, 32)
|
||||
}
|
||||
|
||||
// Write the last 32 bytes
|
||||
mstore(dEnd, last)
|
||||
}
|
||||
} else {
|
||||
assembly {
|
||||
// We subtract 32 from `sEnd` and `dEnd` because those
|
||||
// are the starting points when copying a word at the end.
|
||||
length := sub(length, 32)
|
||||
let sEnd := add(source, length)
|
||||
let dEnd := add(dest, length)
|
||||
|
||||
// Remember the first 32 bytes of source
|
||||
// This needs to be done here and not after the loop
|
||||
// because we may have overwritten the first bytes in
|
||||
// source already due to overlap.
|
||||
let first := mload(source)
|
||||
|
||||
// Copy whole words back to front
|
||||
// We use a signed comparisson here to allow dEnd to become
|
||||
// negative (happens when source and dest < 32). Valid
|
||||
// addresses in local memory will never be larger than
|
||||
// 2**255, so they can be safely re-interpreted as signed.
|
||||
// Note: the first check is always true,
|
||||
// this could have been a do-while loop.
|
||||
for {} slt(dest, dEnd) {} {
|
||||
mstore(dEnd, mload(sEnd))
|
||||
sEnd := sub(sEnd, 32)
|
||||
dEnd := sub(dEnd, 32)
|
||||
}
|
||||
|
||||
// Write the first 32 bytes
|
||||
mstore(dest, first)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,7 +14,6 @@ import * as MultiSigWalletWithTimeLock from '../artifacts/MultiSigWalletWithTime
|
||||
import * as TestAssetDataDecoders from '../artifacts/TestAssetDataDecoders.json';
|
||||
import * as TestAssetProxyDispatcher from '../artifacts/TestAssetProxyDispatcher.json';
|
||||
import * as TestLibBytes from '../artifacts/TestLibBytes.json';
|
||||
import * as TestLibMem from '../artifacts/TestLibMem.json';
|
||||
import * as TestLibs from '../artifacts/TestLibs.json';
|
||||
import * as TestSignatureValidator from '../artifacts/TestSignatureValidator.json';
|
||||
import * as TestValidator from '../artifacts/TestValidator.json';
|
||||
@@ -40,7 +39,6 @@ export const artifacts = {
|
||||
TestAssetProxyDispatcher: (TestAssetProxyDispatcher as any) as ContractArtifact,
|
||||
TestAssetDataDecoders: (TestAssetDataDecoders as any) as ContractArtifact,
|
||||
TestLibBytes: (TestLibBytes as any) as ContractArtifact,
|
||||
TestLibMem: (TestLibMem as any) as ContractArtifact,
|
||||
TestLibs: (TestLibs as any) as ContractArtifact,
|
||||
TestSignatureValidator: (TestSignatureValidator as any) as ContractArtifact,
|
||||
TestValidator: (TestValidator as any) as ContractArtifact,
|
||||
|
@@ -92,7 +92,6 @@ export enum ContractName {
|
||||
Arbitrage = 'Arbitrage',
|
||||
TestAssetDataDecoders = 'TestAssetDataDecoders',
|
||||
TestAssetProxyDispatcher = 'TestAssetProxyDispatcher',
|
||||
TestLibMem = 'TestLibMem',
|
||||
TestLibs = 'TestLibs',
|
||||
TestSignatureValidator = 'TestSignatureValidator',
|
||||
ERC20Proxy = 'ERC20Proxy',
|
||||
|
@@ -27,7 +27,6 @@ describe('TestAssetDataDecoders', () => {
|
||||
// Setup accounts & addresses
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
testAddress = accounts[0];
|
||||
// Deploy TestLibMem
|
||||
testAssetProxyDecoder = await TestAssetDataDecodersContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestAssetDataDecoders,
|
||||
provider,
|
||||
|
@@ -17,6 +17,12 @@ chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
|
||||
// BUG: Ideally we would use Buffer.from(memory).toString('hex')
|
||||
// https://github.com/Microsoft/TypeScript/issues/23155
|
||||
const toHex = (buf: Uint8Array): string => buf.reduce((a, v) => a + ('00' + v.toString(16)).slice(-2), '0x');
|
||||
|
||||
const fromHex = (str: string): Uint8Array => Uint8Array.from(Buffer.from(str.slice(2), 'hex'));
|
||||
|
||||
describe('LibBytes', () => {
|
||||
let libBytes: TestLibBytesContract;
|
||||
const byteArrayShorterThan32Bytes = '0x012345';
|
||||
@@ -617,5 +623,170 @@ describe('LibBytes', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('memCopy', () => {
|
||||
// Create memory 0x000102...FF
|
||||
const memSize = 256;
|
||||
const memory = new Uint8Array(memSize).map((_, i) => i);
|
||||
const memHex = toHex(memory);
|
||||
|
||||
// Reference implementation to test against
|
||||
const refMemcpy = (mem: Uint8Array, dest: number, source: number, length: number): Uint8Array =>
|
||||
Uint8Array.from(mem).copyWithin(dest, source, source + length);
|
||||
|
||||
// Test vectors: destination, source, length, job description
|
||||
type Tests = Array<[number, number, number, string]>;
|
||||
|
||||
const test = (tests: Tests) =>
|
||||
tests.forEach(([dest, source, length, job]) =>
|
||||
it(job, async () => {
|
||||
const expected = refMemcpy(memory, dest, source, length);
|
||||
const resultStr = await libBytes.testMemcpy.callAsync(
|
||||
memHex,
|
||||
new BigNumber(dest),
|
||||
new BigNumber(source),
|
||||
new BigNumber(length),
|
||||
);
|
||||
const result = fromHex(resultStr);
|
||||
expect(result).to.deep.equal(expected);
|
||||
}),
|
||||
);
|
||||
|
||||
test([[0, 0, 0, 'copies zero bytes with overlap']]);
|
||||
|
||||
describe('copies forward', () =>
|
||||
test([
|
||||
[128, 0, 0, 'zero bytes'],
|
||||
[128, 0, 1, 'one byte'],
|
||||
[128, 0, 11, 'eleven bytes'],
|
||||
[128, 0, 31, 'thirty-one bytes'],
|
||||
[128, 0, 32, 'one word'],
|
||||
[128, 0, 64, 'two words'],
|
||||
[128, 0, 96, 'three words'],
|
||||
[128, 0, 33, 'one word and one byte'],
|
||||
[128, 0, 72, 'two words and eight bytes'],
|
||||
[128, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward within one word', () =>
|
||||
test([
|
||||
[16, 0, 0, 'zero bytes'],
|
||||
[16, 0, 1, 'one byte'],
|
||||
[16, 0, 11, 'eleven bytes'],
|
||||
[16, 0, 16, 'sixteen bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with one byte overlap', () =>
|
||||
test([
|
||||
[0, 0, 1, 'one byte'],
|
||||
[10, 0, 11, 'eleven bytes'],
|
||||
[30, 0, 31, 'thirty-one bytes'],
|
||||
[31, 0, 32, 'one word'],
|
||||
[32, 0, 33, 'one word and one byte'],
|
||||
[71, 0, 72, 'two words and eight bytes'],
|
||||
[99, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with thirty-one bytes overlap', () =>
|
||||
test([
|
||||
[0, 0, 31, 'thirty-one bytes'],
|
||||
[1, 0, 32, 'one word'],
|
||||
[2, 0, 33, 'one word and one byte'],
|
||||
[41, 0, 72, 'two words and eight bytes'],
|
||||
[69, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with one word overlap', () =>
|
||||
test([
|
||||
[0, 0, 32, 'one word'],
|
||||
[1, 0, 33, 'one word and one byte'],
|
||||
[41, 0, 72, 'two words and eight bytes'],
|
||||
[69, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with one word and one byte overlap', () =>
|
||||
test([
|
||||
[0, 0, 33, 'one word and one byte'],
|
||||
[40, 0, 72, 'two words and eight bytes'],
|
||||
[68, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with two words overlap', () =>
|
||||
test([
|
||||
[0, 0, 64, 'two words'],
|
||||
[8, 0, 72, 'two words and eight bytes'],
|
||||
[36, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward within one word and one byte overlap', () =>
|
||||
test([[0, 0, 1, 'one byte'], [10, 0, 11, 'eleven bytes'], [15, 0, 16, 'sixteen bytes']]));
|
||||
|
||||
describe('copies backward', () =>
|
||||
test([
|
||||
[0, 128, 0, 'zero bytes'],
|
||||
[0, 128, 1, 'one byte'],
|
||||
[0, 128, 11, 'eleven bytes'],
|
||||
[0, 128, 31, 'thirty-one bytes'],
|
||||
[0, 128, 32, 'one word'],
|
||||
[0, 128, 64, 'two words'],
|
||||
[0, 128, 96, 'three words'],
|
||||
[0, 128, 33, 'one word and one byte'],
|
||||
[0, 128, 72, 'two words and eight bytes'],
|
||||
[0, 128, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward within one word', () =>
|
||||
test([
|
||||
[0, 16, 0, 'zero bytes'],
|
||||
[0, 16, 1, 'one byte'],
|
||||
[0, 16, 11, 'eleven bytes'],
|
||||
[0, 16, 16, 'sixteen bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with one byte overlap', () =>
|
||||
test([
|
||||
[0, 0, 1, 'one byte'],
|
||||
[0, 10, 11, 'eleven bytes'],
|
||||
[0, 30, 31, 'thirty-one bytes'],
|
||||
[0, 31, 32, 'one word'],
|
||||
[0, 32, 33, 'one word and one byte'],
|
||||
[0, 71, 72, 'two words and eight bytes'],
|
||||
[0, 99, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with thirty-one bytes overlap', () =>
|
||||
test([
|
||||
[0, 0, 31, 'thirty-one bytes'],
|
||||
[0, 1, 32, 'one word'],
|
||||
[0, 2, 33, 'one word and one byte'],
|
||||
[0, 41, 72, 'two words and eight bytes'],
|
||||
[0, 69, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with one word overlap', () =>
|
||||
test([
|
||||
[0, 0, 32, 'one word'],
|
||||
[0, 1, 33, 'one word and one byte'],
|
||||
[0, 41, 72, 'two words and eight bytes'],
|
||||
[0, 69, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with one word and one byte overlap', () =>
|
||||
test([
|
||||
[0, 0, 33, 'one word and one byte'],
|
||||
[0, 40, 72, 'two words and eight bytes'],
|
||||
[0, 68, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with two words overlap', () =>
|
||||
test([
|
||||
[0, 0, 64, 'two words'],
|
||||
[0, 8, 72, 'two words and eight bytes'],
|
||||
[0, 36, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward within one word and one byte overlap', () =>
|
||||
test([[0, 0, 1, 'one byte'], [0, 10, 11, 'eleven bytes'], [0, 15, 16, 'sixteen bytes']]));
|
||||
});
|
||||
});
|
||||
// tslint:disable:max-file-line-count
|
||||
|
@@ -1,190 +0,0 @@
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { TestLibMemContract } from '../../src/generated_contract_wrappers/test_lib_mem';
|
||||
import { artifacts } from '../../src/utils/artifacts';
|
||||
import { chaiSetup } from '../../src/utils/chai_setup';
|
||||
import { provider, txDefaults } from '../../src/utils/web3_wrapper';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
// BUG: Ideally we would use Buffer.from(memory).toString('hex')
|
||||
// https://github.com/Microsoft/TypeScript/issues/23155
|
||||
const toHex = (buf: Uint8Array): string => buf.reduce((a, v) => a + ('00' + v.toString(16)).slice(-2), '0x');
|
||||
|
||||
const fromHex = (str: string): Uint8Array => Uint8Array.from(Buffer.from(str.slice(2), 'hex'));
|
||||
|
||||
describe('LibMem', () => {
|
||||
let testLibMem: TestLibMemContract;
|
||||
|
||||
before(async () => {
|
||||
// Deploy TestLibMem
|
||||
testLibMem = await TestLibMemContract.deployFrom0xArtifactAsync(artifacts.TestLibMem, provider, txDefaults);
|
||||
});
|
||||
|
||||
describe('memCopy', () => {
|
||||
// Create memory 0x000102...FF
|
||||
const memSize = 256;
|
||||
const memory = new Uint8Array(memSize).map((_, i) => i);
|
||||
const memHex = toHex(memory);
|
||||
|
||||
// Reference implementation to test against
|
||||
const refMemcpy = (mem: Uint8Array, dest: number, source: number, length: number): Uint8Array =>
|
||||
Uint8Array.from(mem).copyWithin(dest, source, source + length);
|
||||
|
||||
// Test vectors: destination, source, length, job description
|
||||
type Tests = Array<[number, number, number, string]>;
|
||||
|
||||
const test = (tests: Tests) =>
|
||||
tests.forEach(([dest, source, length, job]) =>
|
||||
it(job, async () => {
|
||||
const expected = refMemcpy(memory, dest, source, length);
|
||||
const resultStr = await testLibMem.testMemcpy.callAsync(
|
||||
memHex,
|
||||
new BigNumber(dest),
|
||||
new BigNumber(source),
|
||||
new BigNumber(length),
|
||||
);
|
||||
const result = fromHex(resultStr);
|
||||
expect(result).to.deep.equal(expected);
|
||||
}),
|
||||
);
|
||||
|
||||
test([[0, 0, 0, 'copies zero bytes with overlap']]);
|
||||
|
||||
describe('copies forward', () =>
|
||||
test([
|
||||
[128, 0, 0, 'zero bytes'],
|
||||
[128, 0, 1, 'one byte'],
|
||||
[128, 0, 11, 'eleven bytes'],
|
||||
[128, 0, 31, 'thirty-one bytes'],
|
||||
[128, 0, 32, 'one word'],
|
||||
[128, 0, 64, 'two words'],
|
||||
[128, 0, 96, 'three words'],
|
||||
[128, 0, 33, 'one word and one byte'],
|
||||
[128, 0, 72, 'two words and eight bytes'],
|
||||
[128, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward within one word', () =>
|
||||
test([
|
||||
[16, 0, 0, 'zero bytes'],
|
||||
[16, 0, 1, 'one byte'],
|
||||
[16, 0, 11, 'eleven bytes'],
|
||||
[16, 0, 16, 'sixteen bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with one byte overlap', () =>
|
||||
test([
|
||||
[0, 0, 1, 'one byte'],
|
||||
[10, 0, 11, 'eleven bytes'],
|
||||
[30, 0, 31, 'thirty-one bytes'],
|
||||
[31, 0, 32, 'one word'],
|
||||
[32, 0, 33, 'one word and one byte'],
|
||||
[71, 0, 72, 'two words and eight bytes'],
|
||||
[99, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with thirty-one bytes overlap', () =>
|
||||
test([
|
||||
[0, 0, 31, 'thirty-one bytes'],
|
||||
[1, 0, 32, 'one word'],
|
||||
[2, 0, 33, 'one word and one byte'],
|
||||
[41, 0, 72, 'two words and eight bytes'],
|
||||
[69, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with one word overlap', () =>
|
||||
test([
|
||||
[0, 0, 32, 'one word'],
|
||||
[1, 0, 33, 'one word and one byte'],
|
||||
[41, 0, 72, 'two words and eight bytes'],
|
||||
[69, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with one word and one byte overlap', () =>
|
||||
test([
|
||||
[0, 0, 33, 'one word and one byte'],
|
||||
[40, 0, 72, 'two words and eight bytes'],
|
||||
[68, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward with two words overlap', () =>
|
||||
test([
|
||||
[0, 0, 64, 'two words'],
|
||||
[8, 0, 72, 'two words and eight bytes'],
|
||||
[36, 0, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward within one word and one byte overlap', () =>
|
||||
test([[0, 0, 1, 'one byte'], [10, 0, 11, 'eleven bytes'], [15, 0, 16, 'sixteen bytes']]));
|
||||
|
||||
describe('copies backward', () =>
|
||||
test([
|
||||
[0, 128, 0, 'zero bytes'],
|
||||
[0, 128, 1, 'one byte'],
|
||||
[0, 128, 11, 'eleven bytes'],
|
||||
[0, 128, 31, 'thirty-one bytes'],
|
||||
[0, 128, 32, 'one word'],
|
||||
[0, 128, 64, 'two words'],
|
||||
[0, 128, 96, 'three words'],
|
||||
[0, 128, 33, 'one word and one byte'],
|
||||
[0, 128, 72, 'two words and eight bytes'],
|
||||
[0, 128, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward within one word', () =>
|
||||
test([
|
||||
[0, 16, 0, 'zero bytes'],
|
||||
[0, 16, 1, 'one byte'],
|
||||
[0, 16, 11, 'eleven bytes'],
|
||||
[0, 16, 16, 'sixteen bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with one byte overlap', () =>
|
||||
test([
|
||||
[0, 0, 1, 'one byte'],
|
||||
[0, 10, 11, 'eleven bytes'],
|
||||
[0, 30, 31, 'thirty-one bytes'],
|
||||
[0, 31, 32, 'one word'],
|
||||
[0, 32, 33, 'one word and one byte'],
|
||||
[0, 71, 72, 'two words and eight bytes'],
|
||||
[0, 99, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with thirty-one bytes overlap', () =>
|
||||
test([
|
||||
[0, 0, 31, 'thirty-one bytes'],
|
||||
[0, 1, 32, 'one word'],
|
||||
[0, 2, 33, 'one word and one byte'],
|
||||
[0, 41, 72, 'two words and eight bytes'],
|
||||
[0, 69, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with one word overlap', () =>
|
||||
test([
|
||||
[0, 0, 32, 'one word'],
|
||||
[0, 1, 33, 'one word and one byte'],
|
||||
[0, 41, 72, 'two words and eight bytes'],
|
||||
[0, 69, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with one word and one byte overlap', () =>
|
||||
test([
|
||||
[0, 0, 33, 'one word and one byte'],
|
||||
[0, 40, 72, 'two words and eight bytes'],
|
||||
[0, 68, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward with two words overlap', () =>
|
||||
test([
|
||||
[0, 0, 64, 'two words'],
|
||||
[0, 8, 72, 'two words and eight bytes'],
|
||||
[0, 36, 100, 'three words and four bytes'],
|
||||
]));
|
||||
|
||||
describe('copies forward within one word and one byte overlap', () =>
|
||||
test([[0, 0, 1, 'one byte'], [0, 10, 11, 'eleven bytes'], [0, 15, 16, 'sixteen bytes']]));
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user