// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "../../@ensdomains/buffer/v0.1.0/Buffer.sol"; /** * @dev A library for populating CBOR encoded payload in Solidity. * * https://datatracker.ietf.org/doc/html/rfc7049 * * The library offers various write* and start* methods to encode values of different types. * The resulted buffer can be obtained with data() method. * Encoding of primitive types is staightforward, whereas encoding of sequences can result * in an invalid CBOR if start/write/end flow is violated. * For the purpose of gas saving, the library does not verify start/write/end flow internally, * except for nested start/end pairs. */ library CBOR { using Buffer for Buffer.buffer; struct CBORBuffer { Buffer.buffer buf; uint256 depth; } uint8 private constant MAJOR_TYPE_INT = 0; uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; uint8 private constant MAJOR_TYPE_BYTES = 2; uint8 private constant MAJOR_TYPE_STRING = 3; uint8 private constant MAJOR_TYPE_ARRAY = 4; uint8 private constant MAJOR_TYPE_MAP = 5; uint8 private constant MAJOR_TYPE_TAG = 6; uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; uint8 private constant TAG_TYPE_BIGNUM = 2; uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; uint8 private constant CBOR_FALSE = 20; uint8 private constant CBOR_TRUE = 21; uint8 private constant CBOR_NULL = 22; uint8 private constant CBOR_UNDEFINED = 23; function create(uint256 capacity) internal pure returns(CBORBuffer memory cbor) { Buffer.init(cbor.buf, capacity); cbor.depth = 0; return cbor; } function data(CBORBuffer memory buf) internal pure returns(bytes memory) { require(buf.depth == 0, "Invalid CBOR"); return buf.buf.buf; } function writeUInt256(CBORBuffer memory buf, uint256 value) internal pure { buf.buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); writeBytes(buf, abi.encode(value)); } function writeInt256(CBORBuffer memory buf, int256 value) internal pure { if (value < 0) { buf.buf.appendUint8( uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM) ); writeBytes(buf, abi.encode(uint256(-1 - value))); } else { writeUInt256(buf, uint256(value)); } } function writeUInt64(CBORBuffer memory buf, uint64 value) internal pure { writeFixedNumeric(buf, MAJOR_TYPE_INT, value); } function writeInt64(CBORBuffer memory buf, int64 value) internal pure { if(value >= 0) { writeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); } else{ writeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(-1 - value)); } } function writeBytes(CBORBuffer memory buf, bytes memory value) internal pure { writeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); buf.buf.append(value); } function writeString(CBORBuffer memory buf, string memory value) internal pure { writeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); buf.buf.append(bytes(value)); } function writeBool(CBORBuffer memory buf, bool value) internal pure { writeContentFree(buf, value ? CBOR_TRUE : CBOR_FALSE); } function writeNull(CBORBuffer memory buf) internal pure { writeContentFree(buf, CBOR_NULL); } function writeUndefined(CBORBuffer memory buf) internal pure { writeContentFree(buf, CBOR_UNDEFINED); } function startArray(CBORBuffer memory buf) internal pure { writeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); buf.depth += 1; } function startFixedArray(CBORBuffer memory buf, uint64 length) internal pure { writeDefiniteLengthType(buf, MAJOR_TYPE_ARRAY, length); } function startMap(CBORBuffer memory buf) internal pure { writeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); buf.depth += 1; } function startFixedMap(CBORBuffer memory buf, uint64 length) internal pure { writeDefiniteLengthType(buf, MAJOR_TYPE_MAP, length); } function endSequence(CBORBuffer memory buf) internal pure { writeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); buf.depth -= 1; } function writeKVString(CBORBuffer memory buf, string memory key, string memory value) internal pure { writeString(buf, key); writeString(buf, value); } function writeKVBytes(CBORBuffer memory buf, string memory key, bytes memory value) internal pure { writeString(buf, key); writeBytes(buf, value); } function writeKVUInt256(CBORBuffer memory buf, string memory key, uint256 value) internal pure { writeString(buf, key); writeUInt256(buf, value); } function writeKVInt256(CBORBuffer memory buf, string memory key, int256 value) internal pure { writeString(buf, key); writeInt256(buf, value); } function writeKVUInt64(CBORBuffer memory buf, string memory key, uint64 value) internal pure { writeString(buf, key); writeUInt64(buf, value); } function writeKVInt64(CBORBuffer memory buf, string memory key, int64 value) internal pure { writeString(buf, key); writeInt64(buf, value); } function writeKVBool(CBORBuffer memory buf, string memory key, bool value) internal pure { writeString(buf, key); writeBool(buf, value); } function writeKVNull(CBORBuffer memory buf, string memory key) internal pure { writeString(buf, key); writeNull(buf); } function writeKVUndefined(CBORBuffer memory buf, string memory key) internal pure { writeString(buf, key); writeUndefined(buf); } function writeKVMap(CBORBuffer memory buf, string memory key) internal pure { writeString(buf, key); startMap(buf); } function writeKVArray(CBORBuffer memory buf, string memory key) internal pure { writeString(buf, key); startArray(buf); } function writeFixedNumeric( CBORBuffer memory buf, uint8 major, uint64 value ) private pure { if (value <= 23) { buf.buf.appendUint8(uint8((major << 5) | value)); } else if (value <= 0xFF) { buf.buf.appendUint8(uint8((major << 5) | 24)); buf.buf.appendInt(value, 1); } else if (value <= 0xFFFF) { buf.buf.appendUint8(uint8((major << 5) | 25)); buf.buf.appendInt(value, 2); } else if (value <= 0xFFFFFFFF) { buf.buf.appendUint8(uint8((major << 5) | 26)); buf.buf.appendInt(value, 4); } else { buf.buf.appendUint8(uint8((major << 5) | 27)); buf.buf.appendInt(value, 8); } } function writeIndefiniteLengthType(CBORBuffer memory buf, uint8 major) private pure { buf.buf.appendUint8(uint8((major << 5) | 31)); } function writeDefiniteLengthType(CBORBuffer memory buf, uint8 major, uint64 length) private pure { writeFixedNumeric(buf, major, length); } function writeContentFree(CBORBuffer memory buf, uint8 value) private pure { buf.buf.appendUint8(uint8((MAJOR_TYPE_CONTENT_FREE << 5) | value)); } }