docs: def read_data_page(file_obj, schema_helper, page_header, column_metadata,
                     dictionary):
      """Read the data page from the given file-like object based upon the parameters.
  
      Metadata in the the schema_helper, page_header, column_metadata, and (optional) dictionary
      are used for parsing data.
  
      Returns a list of values.
      """
      daph = page_header.data_page_header
      raw_bytes = _read_page(file_obj, page_header, column_metadata)
      io_obj = io.BytesIO(raw_bytes)
      vals = []
      debug_logging = logger.isEnabledFor(logging.DEBUG)
  
      if debug_logging:
          logger.debug("  definition_level_encoding: %s",
                       _get_name(parquet_thrift.Encoding, daph.definition_level_encoding))
          logger.debug("  repetition_level_encoding: %s",
                       _get_name(parquet_thrift.Encoding, daph.repetition_level_encoding))
          logger.debug("  encoding: %s", _get_name(parquet_thrift.Encoding, daph.encoding))
  
      # definition levels are skipped if data is required.
      definition_levels = None
      num_nulls = 0
      max_definition_level = -1
      if not schema_helper.is_required(column_metadata.path_in_schema[-1]):
          max_definition_level = schema_helper.max_definition_level(
              column_metadata.path_in_schema)
          bit_width = encoding.width_from_max_int(max_definition_level)
          if debug_logging:
              logger.debug("  max def level: %s   bit_width: %s",
                           max_definition_level, bit_width)
          if bit_width == 0:
              definition_levels = [0] * daph.num_values
          else:
              definition_levels = _read_data(io_obj,
                                             daph.definition_level_encoding,
                                             daph.num_values,
                                             bit_width)[:daph.num_values]
  
          # any thing that isn't at max definition level is a null.
          num_nulls = len(definition_levels) - definition_levels.count(max_definition_level)
          if debug_logging:
              logger.debug("  Definition levels: %s", len(definition_levels))
  
      # repetition levels are skipped if data is at the first level.
      repetition_levels = None  # pylint: disable=unused-variable
      if len(column_metadata.path_in_schema) > 1:
          max_repetition_level = schema_helper.max_repetition_level(
              column_metadata.path_in_schema)
          bit_width = encoding.width_from_max_int(max_repetition_level)
          repetition_levels = _read_data(io_obj,
                                         daph.repetition_level_encoding,
                                         daph.num_values,
                                         bit_width)
  
      # NOTE: The repetition levels aren't yet used.
      if daph.encoding == parquet_thrift.Encoding.PLAIN:
          read_values = encoding.read_plain(io_obj, column_metadata.type, daph.num_values - num_nulls)
          schema_element = schema_helper.schema_element(column_metadata.path_in_schema[-1])
          read_values = convert_column(read_values, schema_element) \
              if schema_element.converted_type is not None else read_values
          if definition_levels:
              itr = iter(read_values)
              vals.extend([next(itr) if level == max_definition_level else None for level in definition_levels])
          else:
              vals.extend(read_values)
          if debug_logging:
              logger.debug("  Values: %s, nulls: %s", len(vals), num_nulls)
  
      elif daph.encoding == parquet_thrift.Encoding.PLAIN_DICTIONARY:
          # bit_width is stored as single byte.
          bit_width = struct.unpack(b"<B", io_obj.read(1))[0]
          if debug_logging:
              logger.debug("bit_width: %d", bit_width)
  
          dict_values_bytes = io_obj.read()
          dict_values_io_obj = io.BytesIO(dict_values_bytes)
          # read_values stores the bit-packed values. If there are definition levels and the data contains nulls,
          # the size of read_values will be less than daph.num_values
          read_values = []
          while dict_values_io_obj.tell() < len(dict_values_bytes):
              read_values.extend(encoding.read_rle_bit_packed_hybrid(
                  dict_values_io_obj, bit_width, len(dict_values_bytes)))
  
          if definition_levels:
              itr = iter(read_values)
              # add the nulls into a new array, values, but using the definition_levels data.
              values = [dictionary[next(itr)] if level == max_definition_level else None for level in definition_levels]
          else:
              values = [dictionary[v] for v in read_values]
  
          # there can be extra values on the end of the array because the last bit-packed chunk may be zero-filled.
          if len(values) > daph.num_values:
              values = values[0: daph.num_values]
          vals.extend(values)
  
          if debug_logging:
              logger.debug("  Read %s values using PLAIN_DICTIONARY encoding and definition levels show %s nulls",
                           len(vals), num_nulls)
  
      else:
          raise ParquetFormatException("Unsupported encoding: %s",
                                       _get_name(parquet_thrift.Encoding, daph.encoding))
      return vals