library Buffer {
    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)))
        }
    }
    
    /**
     * value must be in the closed interval [-999, 999]
     * otherwise only the last 3 digits will be converted
     */
    function numbie(bytes memory buffer, int256 value) internal pure {
        require(hasCapacityFor(buffer, 4), "Buffer.numbie: no capacity");
        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)
            }
            let strIdx := add(mload(add(buffer, 32)), add(buffer, 64))
            strIdx := numbi3(strIdx, value)
            mstore(add(buffer, 32), sub(sub(strIdx, buffer), 64))
        }
    }
}
function numbie(bytes memory buffer, int256 value) internal pure {
    require(hasCapacityFor(buffer, 4), "Buffer.numbie: no capacity");
    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)
        }
        let strIdx := add(mload(add(buffer, 32)), add(buffer, 64))
        strIdx := numbi3(strIdx, value)
        mstore(add(buffer, 32), sub(sub(strIdx, buffer), 64))
    }
}

/**
 * value must be in the closed interval [-999, 999]
 * otherwise only the last 3 digits will be converted
 */