text: // Use Uint8Array directly in the browser, use Buffer in Node.js but don't
  // speak its name directly to avoid bundlers pulling in the `Buffer` polyfill
  
  // @ts-ignore
  export const useBuffer = globalThis.process &&
    // @ts-ignore
    !globalThis.process.browser &&
    // @ts-ignore
    globalThis.Buffer &&
    // @ts-ignore
    typeof globalThis.Buffer.isBuffer === 'function'
  
  const textDecoder = new TextDecoder()
  const textEncoder = new TextEncoder()
  
  /**
   * @param {Uint8Array} buf
   * @returns {boolean}
   */
  function isBuffer (buf) {
    // @ts-ignore
    return useBuffer && globalThis.Buffer.isBuffer(buf)
  }
  
  /**
   * @param {Uint8Array|number[]} buf
   * @returns {Uint8Array}
   */
  export function asU8A (buf) {
    /* c8 ignore next */
    if (!(buf instanceof Uint8Array)) {
      return Uint8Array.from(buf)
    }
    return isBuffer(buf) ? new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength) : buf
  }
  
  export const toString = useBuffer
    ? // eslint-disable-line operator-linebreak
      /**
       * @param {Uint8Array} bytes
       * @param {number} start
       * @param {number} end
       */
      (bytes, start, end) => {
        return end - start > 64
          ? // eslint-disable-line operator-linebreak
            // @ts-ignore
            globalThis.Buffer.from(bytes.subarray(start, end)).toString('utf8')
          : utf8Slice(bytes, start, end)
      }
    /* c8 ignore next 11 */
    : // eslint-disable-line operator-linebreak
      /**
       * @param {Uint8Array} bytes
       * @param {number} start
       * @param {number} end
       */
      (bytes, start, end) => {
        return end - start > 64
          ? textDecoder.decode(bytes.subarray(start, end))
          : utf8Slice(bytes, start, end)
      }
  
  export const fromString = useBuffer
    ? // eslint-disable-line operator-linebreak
      /**
       * @param {string} string
       */
      (string) => {
        return string.length > 64
          ? // eslint-disable-line operator-linebreak
            // @ts-ignore
            globalThis.Buffer.from(string)
          : utf8ToBytes(string)
      }
    /* c8 ignore next 7 */
    : // eslint-disable-line operator-linebreak
      /**
       * @param {string} string
       */
      (string) => {
        return string.length > 64 ? textEncoder.encode(string) : utf8ToBytes(string)
      }
  
  /**
   * Buffer variant not fast enough for what we need
   * @param {number[]} arr
   * @returns {Uint8Array}
   */
  export const fromArray = (arr) => {
    return Uint8Array.from(arr)
  }
  
  export const slice = useBuffer
    ? // eslint-disable-line operator-linebreak
      /**
       * @param {Uint8Array} bytes
       * @param {number} start
       * @param {number} end
       */
      (bytes, start, end) => {
        if (isBuffer(bytes)) {
          return new Uint8Array(bytes.subarray(start, end))
        }
        return bytes.slice(start, end)
      }
    /* c8 ignore next 9 */
    : // eslint-disable-line operator-linebreak
      /**
       * @param {Uint8Array} bytes
       * @param {number} start
       * @param {number} end
       */
      (bytes, start, end) => {
        return bytes.slice(start, end)
      }
  
  export const concat = useBuffer
    ? // eslint-disable-line operator-linebreak
      /**
       * @param {Uint8Array[]} chunks
       * @param {number} length
       * @returns {Uint8Array}
       */
      (chunks, length) => {
        // might get a stray plain Array here
        /* c8 ignore next 1 */
        chunks = chunks.map((c) => c instanceof Uint8Array
          ? c
          // this case is occasionally missed during test runs so becomes coverage-flaky
          /* c8 ignore next 4 */
          : // eslint-disable-line operator-linebreak
          // @ts-ignore
          globalThis.Buffer.from(c))
        // @ts-ignore
        return asU8A(globalThis.Buffer.concat(chunks, length))
      }
    /* c8 ignore next 19 */
    : // eslint-disable-line operator-linebreak
      /**
       * @param {Uint8Array[]} chunks
       * @param {number} length
       * @returns {Uint8Array}
       */
      (chunks, length) => {
        const out = new Uint8Array(length)
        let off = 0
        for (let b of chunks) {
          if (off + b.length > out.length) {
            // final chunk that's bigger than we need
            b = b.subarray(0, out.length - off)
          }
          out.set(b, off)
          off += b.length
        }
        return out
      }
  
  export const alloc = useBuffer
    ? // eslint-disable-line operator-linebreak
      /**
       * @param {number} size
       * @returns {Uint8Array}
       */
      (size) => {
        // we always write over the contents we expose so this should be safe
        // @ts-ignore
        return globalThis.Buffer.allocUnsafe(size)
      }
    /* c8 ignore next 8 */
    : // eslint-disable-line operator-linebreak
      /**
       * @param {number} size
       * @returns {Uint8Array}
       */
      (size) => {
        return new Uint8Array(size)
      }
  
  export const toHex = useBuffer
    ? // eslint-disable-line operator-linebreak
      /**
       * @param {Uint8Array} d
       * @returns {string}
       */
      (d) => {
        if (typeof d === 'string') {
          return d
        }
        // @ts-ignore
        return globalThis.Buffer.from(toBytes(d)).toString('hex')
      }
    /* c8 ignore next 12 */
    : // eslint-disable-line operator-linebreak
      /**
       * @param {Uint8Array} d
       * @returns {string}
       */
      (d) => {
        if (typeof d === 'string') {
          return d
        }
        // @ts-ignore not smart enough to figure this out
        return Array.prototype.reduce.call(toBytes(d), (p, c) => `${p}${c.toString(16).padStart(2, '0')}`, '')
      }
  
  export const fromHex = useBuffer
    ? // eslint-disable-line operator-linebreak
    /**
     * @param {string|Uint8Array} hex
     * @returns {Uint8Array}
     */
      (hex) => {
        if (hex instanceof Uint8Array) {
          return hex
        }
        // @ts-ignore
        return globalThis.Buffer.from(hex, 'hex')
      }
    /* c8 ignore next 17 */
    : // eslint-disable-line operator-linebreak
    /**
     * @param {string|Uint8Array} hex
     * @returns {Uint8Array}
     */
      (hex) => {
        if (hex instanceof Uint8Array) {
          return hex
        }
        if (!hex.length) {
          return new Uint8Array(0)
        }
        return new Uint8Array(hex.split('')
          .map((/** @type {string} */ c, /** @type {number} */ i, /** @type {string[]} */ d) => i % 2 === 0 ? `0x${c}${d[i + 1]}` : '')
          .filter(Boolean)
          .map((/** @type {string} */ e) => parseInt(e, 16)))
      }
  
  /**
   * @param {Uint8Array|ArrayBuffer|ArrayBufferView} obj
   * @returns {Uint8Array}
   */
  function toBytes (obj) {
    if (obj instanceof Uint8Array && obj.constructor.name === 'Uint8Array') {
      return obj
    }
    if (obj instanceof ArrayBuffer) {
      return new Uint8Array(obj)
    }
    if (ArrayBuffer.isView(obj)) {
      return new Uint8Array(obj.buffer, obj.byteOffset, obj.byteLength)
    }
    /* c8 ignore next */
    throw new Error('Unknown type, must be binary type')
  }
  
  /**
   * @param {Uint8Array} b1
   * @param {Uint8Array} b2
   * @returns {number}
   */
  export function compare (b1, b2) {
    /* c8 ignore next 5 */
    if (isBuffer(b1) && isBuffer(b2)) {
      // probably not possible to get here in the current API
      // @ts-ignore Buffer
      return b1.compare(b2)
    }
    for (let i = 0; i < b1.length; i++) {
      if (b1[i] === b2[i]) {
        continue
      }
      return b1[i] < b2[i] ? -1 : 1
    } /* c8 ignore next 3 */
    return 0
  }
  
  // The below code is mostly taken from https://github.com/feross/buffer
  // Licensed MIT. Copyright (c) Feross Aboukhadijeh
  
  /**
   * @param {string} string
   * @param {number} [units]
   * @returns {number[]}
   */
  function utf8ToBytes (string, units = Infinity) {
    let codePoint
    const length = string.length
    let leadSurrogate = null
    const bytes = []
  
    for (let i = 0; i < length; ++i) {
      codePoint = string.charCodeAt(i)
  
      // is surrogate component
      if (codePoint > 0xd7ff && codePoint < 0xe000) {
        // last char was a lead
        if (!leadSurrogate) {
          // no lead yet
          /* c8 ignore next 9 */
          if (codePoint > 0xdbff) {
            // unexpected trail
            if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd)
            continue
          } else if (i + 1 === length) {
            // unpaired lead
            if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd)
            continue
          }
  
          // valid lead
          leadSurrogate = codePoint
  
          continue
        }
  
        // 2 leads in a row
        /* c8 ignore next 5 */
        if (codePoint < 0xdc00) {
          if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd)
          leadSurrogate = codePoint
          continue
        }
  
        // valid surrogate pair
        codePoint = (leadSurrogate - 0xd800 << 10 | codePoint - 0xdc00) + 0x10000
      /* c8 ignore next 4 */
      } else if (leadSurrogate) {
        // valid bmp char, but last char was a lead
        if ((units -= 3) > -1) bytes.push(0xef, 0xbf, 0xbd)
      }
  
      leadSurrogate = null
  
      // encode utf8
      if (codePoint < 0x80) {
        /* c8 ignore next 1 */
        if ((units -= 1) < 0) break
        bytes.push(codePoint)
      } else if (codePoint < 0x800) {
        /* c8 ignore next 1 */
        if ((units -= 2) < 0) break
        bytes.push(
          codePoint >> 0x6 | 0xc0,
          codePoint & 0x3f | 0x80
        )
      } else if (codePoint < 0x10000) {
        /* c8 ignore next 1 */
        if ((units -= 3) < 0) break
        bytes.push(
          codePoint >> 0xc | 0xe0,
          codePoint >> 0x6 & 0x3f | 0x80,
          codePoint & 0x3f | 0x80
        )
      /* c8 ignore next 9 */
      } else if (codePoint < 0x110000) {
        if ((units -= 4) < 0) break
        bytes.push(
          codePoint >> 0x12 | 0xf0,
          codePoint >> 0xc & 0x3f | 0x80,
          codePoint >> 0x6 & 0x3f | 0x80,
          codePoint & 0x3f | 0x80
        )
      } else {
        /* c8 ignore next 2 */
        throw new Error('Invalid code point')
      }
    }
  
    return bytes
  }
  
  /**
   * @param {Uint8Array} buf
   * @param {number} offset
   * @param {number} end
   * @returns {string}
   */
  function utf8Slice (buf, offset, end) {
    const res = []
  
    while (offset < end) {
      const firstByte = buf[offset]
      let codePoint = null
      let bytesPerSequence = (firstByte > 0xef) ? 4 : (firstByte > 0xdf) ? 3 : (firstByte > 0xbf) ? 2 : 1
  
      if (offset + bytesPerSequence <= end) {
        let secondByte, thirdByte, fourthByte, tempCodePoint
  
        switch (bytesPerSequence) {
          case 1:
            if (firstByte < 0x80) {
              codePoint = firstByte
            }
            break
          case 2:
            secondByte = buf[offset + 1]
            if ((secondByte & 0xc0) === 0x80) {
              tempCodePoint = (firstByte & 0x1f) << 0x6 | (secondByte & 0x3f)
              if (tempCodePoint > 0x7f) {
                codePoint = tempCodePoint
              }
            }
            break
          case 3:
            secondByte = buf[offset + 1]
            thirdByte = buf[offset + 2]
            if ((secondByte & 0xc0) === 0x80 && (thirdByte & 0xc0) === 0x80) {
              tempCodePoint = (firstByte & 0xf) << 0xc | (secondByte & 0x3f) << 0x6 | (thirdByte & 0x3f)
              /* c8 ignore next 3 */
              if (tempCodePoint > 0x7ff && (tempCodePoint < 0xd800 || tempCodePoint > 0xdfff)) {
                codePoint = tempCodePoint
              }
            }
            break
          case 4:
            secondByte = buf[offset + 1]
            thirdByte = buf[offset + 2]
            fourthByte = buf[offset + 3]
            if ((secondByte & 0xc0) === 0x80 && (thirdByte & 0xc0) === 0x80 && (fourthByte & 0xc0) === 0x80) {
              tempCodePoint = (firstByte & 0xf) << 0x12 | (secondByte & 0x3f) << 0xc | (thirdByte & 0x3f) << 0x6 | (fourthByte & 0x3f)
              if (tempCodePoint > 0xffff && tempCodePoint < 0x110000) {
                codePoint = tempCodePoint
              }
            }
        }
      }
  
      /* c8 ignore next 5 */
      if (codePoint === null) {
        // we did not generate a valid codePoint so insert a
        // replacement char (U+FFFD) and advance only 1 byte
        codePoint = 0xfffd
        bytesPerSequence = 1
      } else if (codePoint > 0xffff) {
        // encode to utf16 (surrogate pair dance)
        codePoint -= 0x10000
        res.push(codePoint >>> 10 & 0x3ff | 0xd800)
        codePoint = 0xdc00 | codePoint & 0x3ff
      }
  
      res.push(codePoint)
      offset += bytesPerSequence
    }
  
    return decodeCodePointsArray(res)
  }
  
  // Based on http://stackoverflow.com/a/22747272/680742, the browser with
  // the lowest limit is Chrome, with 0x10000 args.
  // We go 1 magnitude less, for safety
  const MAX_ARGUMENTS_LENGTH = 0x1000
  
  /**
   * @param {number[]} codePoints
   * @returns {string}
   */
  export function decodeCodePointsArray (codePoints) {
    const len = codePoints.length
    if (len <= MAX_ARGUMENTS_LENGTH) {
      return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
    }
    /* c8 ignore next 10 */
    // Decode in chunks to avoid "call stack size exceeded".
    let res = ''
    let i = 0
    while (i < len) {
      res += String.fromCharCode.apply(
        String,
        codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
      )
    }
    return res
  }
  