code: def as_buffer(
      data: Union[int, Any],
      *,
      size: Optional[int] = None,
      owner: Optional[object] = None,
      exposed: bool = False,
  ) -> Buffer:
      """Factory function to wrap `data` in a Buffer object.
  
      If `data` isn't a buffer already, a new buffer that points to the memory of
      `data` is created. If `data` represents host memory, it is copied to a new
      `rmm.DeviceBuffer` device allocation. Otherwise, the memory of `data` is
      **not** copied, instead the new buffer keeps a reference to `data` in order
      to retain its lifetime.
  
      If `data` is an integer, it is assumed to point to device memory.
  
      Raises ValueError if `data` isn't C-contiguous.
  
      If copy-on-write is enabled, an ExposureTrackedBuffer is returned.
  
      If spilling is enabled, a SpillableBuffer that refers to a
      SpillableBufferOwner is returned. If `data` is owned by a spillable buffer,
      it must either be "exposed" or spill locked (called within an
      acquire_spill_lock context). This is to guarantee that the memory of `data`
      isn't spilled before this function gets to calculate the offset of the new
      SpillableBuffer.
  
  
      Parameters
      ----------
      data : int or buffer-like or array-like
          An integer representing a pointer to device memory or a buffer-like
          or array-like object. When not an integer, `size` and `owner` must
          be None.
      size : int, optional
          Size of device memory in bytes. Must be specified if `data` is an
          integer.
      owner : object, optional
          Python object to which the lifetime of the memory allocation is tied.
          A reference to this object is kept in the returned Buffer.
      exposed : bool, optional
          Mark the buffer as permanently exposed. This is used by
          ExposureTrackedBuffer to determine when a deep copy is required and
          by SpillableBuffer to mark the buffer unspillable.
  
      Return
      ------
      Buffer
          A buffer instance that represents the device memory of `data`.
      """
  
      if isinstance(data, Buffer):
          return data
  
      # We handle the integer argument in the factory function by wrapping
      # the pointer in a `__cuda_array_interface__` exposing object so that
      # the Buffer (and its sub-classes) do not have to.
      if isinstance(data, int):
          if size is None:
              raise ValueError(
                  "size must be specified when `data` is an integer"
              )
          data = cuda_array_interface_wrapper(
              ptr=data, size=size, owner=owner
          )
      elif size is not None or owner is not None:
          raise ValueError(
              "`size` and `owner` must be None when "
              "`data` is a buffer-like or array-like object"
          )
  
      # Find the buffer types to return based on the current config
      owner_class: Type[BufferOwner]
      buffer_class: Type[Buffer]
      if get_global_manager() is not None:
          owner_class = SpillableBufferOwner
          buffer_class = SpillableBuffer
      elif get_option("copy_on_write"):
          owner_class = BufferOwner
          buffer_class = ExposureTrackedBuffer
      else:
          owner_class = BufferOwner
          buffer_class = Buffer
  
      # Handle host memory,
      if not hasattr(data, "__cuda_array_interface__"):
          if exposed:
              raise ValueError("cannot created exposed host memory")
          return buffer_class(owner=owner_class._from_host_memory(data))
  
      # Check if `data` is owned by a known class
      owner = get_buffer_owner(data)
      if owner is None:  # `data` is new device memory
          return buffer_class(
              owner=owner_class._from_device_memory(
                  data, exposed=exposed
              )
          )
  
      # At this point, we know that `data` is owned by a known class, which
      # should be the same class as specified by the current config (see above)
      assert owner.__class__ is owner_class
      if (
          isinstance(owner, SpillableBufferOwner)
          and not owner.exposed
          and get_spill_lock() is None
      ):
          raise ValueError(
              "An owning spillable buffer must "
              "either be exposed or spill locked."
          )
      ptr, size = get_ptr_and_size(data.__cuda_array_interface__)
      base_ptr = owner.get_ptr(mode="read")
      if size > 0 and base_ptr == 0:
          raise ValueError(
              "Cannot create a non-empty slice of a null buffer"
          )
      return buffer_class(owner=owner, offset=ptr - base_ptr, size=size)
  
file_path: cudf/python/cudf/cudf/core/buffer/utils.py
imports: from __future__ import annotations
  import threading
  from contextlib import ContextDecorator
  from typing import Any, Dict, Optional, Tuple, Type, Union
  from cudf.core.buffer.buffer import Buffer, BufferOwner, cuda_array_interface_wrapper, get_ptr_and_size
  from cudf.core.buffer.exposure_tracked_buffer import ExposureTrackedBuffer
  from cudf.core.buffer.spill_manager import get_global_manager
  from cudf.core.buffer.spillable_buffer import SpillableBuffer, SpillableBufferOwner, SpillLock
  from cudf.options import get_option
label_desc: Use image node_id 2 for calling a global function with example usage: as_buffer(data) and returns: buffer_class, data, buffer_class, buffer_class