Proxy Router

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.0;

contract Impl {
    
    event LogData(bytes lData);
 
    
    function log() public {
        emit LogData(msg.data);
    }
}

contract ProxyRouter {
    function _delegate(address implementation, bytes memory callData) internal virtual {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            // Call the implementation.
            // out and outsize are 0 because we don't know the size yet.
            // let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
            // delegatecall(g, a, in, insize, out, outsize)
            let result := delegatecall(gas(), implementation, add(callData, 0x20), mload(callData), 0, 0)

            // Copy the returned data.
            returndatacopy(0, 0, returndatasize())

            switch result
            // delegatecall returns 0 on error.
            case 0 { revert(0, returndatasize()) }
            default { return(0, returndatasize()) }
        }
    }
    
    event Hello(address indexed impl, bytes pData, bytes mData);
    
    function hello(address impl, bytes memory pData) public {
        emit Hello(impl, pData, msg.data);
        
        _delegate(impl, pData);
    }
}

I write a ProxyRouter by Proxy.sol, but why I need add 0x20 for callData when call delegatecall in the assembly?

Is there something error do like this?

And, do we have a ProxyRouter contract so that I can add different logic?

such as:

    mapping (uint256 => address) public core;

    function router(uint256 userType, bytes memory pData) public {
        address impl = core[userType];
        require(impl != address(0));
       
       _delegate(impl, pData);
    }

in this case, I only authorize tokens to the router contract once and then use difference logic.