content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.carbondata.core.datastore.chunk.reader.dimension.v3;
  
  import java.io.ByteArrayInputStream;
  import java.io.IOException;
  import java.nio.ByteBuffer;
  
  import org.apache.carbondata.core.datastore.FileReader;
  import org.apache.carbondata.core.datastore.ReusableDataBuffer;
  import org.apache.carbondata.core.datastore.chunk.DimensionColumnPage;
  import org.apache.carbondata.core.datastore.chunk.impl.DimensionRawColumnChunk;
  import org.apache.carbondata.core.datastore.compression.CompressorFactory;
  import org.apache.carbondata.core.metadata.blocklet.BlockletInfo;
  import org.apache.carbondata.core.util.CarbonMetadataUtil;
  import org.apache.carbondata.core.util.CarbonUtil;
  import org.apache.carbondata.format.DataChunk2;
  import org.apache.carbondata.format.DataChunk3;
  import org.apache.carbondata.format.Encoding;
  
  /**
   * Dimension column V3 Reader class which will be used to read and uncompress
   * V3 format data. It reads the data in each page at once unlike whole blocklet. It is
   * used for memory constraint operations like compaction.
   * data format
   * Data Format
   * <FileHeader>
   * <Column1 Data ChunkV3><Column1<Page1><Page2><Page3><Page4>>
   * <Column2 Data ChunkV3><Column2<Page1><Page2><Page3><Page4>>
   * <Column3 Data ChunkV3><Column3<Page1><Page2><Page3><Page4>>
   * <Column4 Data ChunkV3><Column4<Page1><Page2><Page3><Page4>>
   * <File Footer>
   */
  public class DimensionChunkPageReaderV3 extends DimensionChunkReaderV3 {
  
    /**
     * end position of last dimension in carbon data file
     */
    private long lastDimensionOffsets;
  
    public DimensionChunkPageReaderV3(BlockletInfo blockletInfo, String filePath) {
      super(blockletInfo, filePath);
      lastDimensionOffsets = blockletInfo.getDimensionOffset();
    }
  
    /**
     * Below method will be used to read the dimension column data form carbon data file
     * Steps for reading
     * 1. Get the length of the data to be read
     * 2. Allocate the direct buffer
     * 3. read the data from file
     * 4. Get the data chunk object from data read
     * 5. Create the raw chunk object and fill the details
     *
     * @param fileReader          reader for reading the column from carbon data file
     * @param blockletColumnIndex blocklet index of the column in carbon data file
     * @return dimension raw chunk
     */
    @Override
    public DimensionRawColumnChunk readRawDimensionChunk(FileReader fileReader,
        int blockletColumnIndex) throws IOException {
      // get the current dimension offset
      long currentDimensionOffset = dimensionChunksOffset.get(blockletColumnIndex);
      int length = 0;
      // to calculate the length of the data to be read
      // column other than last column we can subtract the offset of current column with
      // next column and get the total length.
      // but for last column we need to use lastDimensionOffset which is the end position
      // of the last dimension, we can subtract current dimension offset from lastDimensionOffset
      if (dimensionChunksOffset.size() - 1 == blockletColumnIndex) {
        length = (int) (lastDimensionOffsets - currentDimensionOffset);
      } else {
        length = (int) (dimensionChunksOffset.get(blockletColumnIndex + 1) - currentDimensionOffset);
      }
      ByteBuffer buffer;
      // read the data from carbon data file
      synchronized (fileReader) {
        buffer = fileReader.readByteBuffer(filePath, currentDimensionOffset,
            dimensionChunksLength.get(blockletColumnIndex));
      }
      // get the data chunk which will have all the details about the data pages
      DataChunk3 dataChunk = CarbonUtil.readDataChunk3(new ByteArrayInputStream(buffer.array()));
  
      return getDimensionRawColumnChunk(fileReader, blockletColumnIndex, currentDimensionOffset,
          length, null, dataChunk);
    }
  
    /**
     * Below method will be used to read the multiple dimension column data in group
     * and divide into dimension raw chunk object
     * Steps for reading
     * 1. Get the length of the data to be read
     * 2. Allocate the direct buffer
     * 3. read the data from file
     * 4. Get the data chunk object from file for each column
     * 5. Create the raw chunk object and fill the details for each column
     * 6. increment the offset of the data
     *
     * @param fileReader      reader which will be used to read the dimension columns data from file
     * @param startBlockletColumnIndex blocklet index of the first dimension column
     * @param endBlockletColumnIndex   blocklet index of the last dimension column
     * @ DimensionRawColumnChunk array
     */
    protected DimensionRawColumnChunk[] readRawDimensionChunksInGroup(FileReader fileReader,
        int startBlockletColumnIndex, int endBlockletColumnIndex) throws IOException {
      // create raw chunk for each dimension column
      DimensionRawColumnChunk[] dimensionDataChunks =
          new DimensionRawColumnChunk[endBlockletColumnIndex - startBlockletColumnIndex + 1];
      int index = 0;
      for (int i = startBlockletColumnIndex; i <= endBlockletColumnIndex; i++) {
        dimensionDataChunks[index] = readRawDimensionChunk(fileReader, i);
        index++;
      }
      return dimensionDataChunks;
    }
  
    /**
     * Below method will be used to convert the compressed dimension chunk raw data to actual data
     *
     * @param dimensionRawColumnChunk dimension raw chunk
     * @param pageNumber              number
     * @return DimensionColumnDataChunk
     */
    @Override
    public DimensionColumnPage decodeColumnPage(
        DimensionRawColumnChunk dimensionRawColumnChunk, int pageNumber,
        ReusableDataBuffer reusableDataBuffer)
        throws IOException {
      // data chunk of page
      DataChunk2 pageMetadata = null;
      // data chunk of blocklet column
      DataChunk3 dataChunk3 = dimensionRawColumnChunk.getDataChunkV3();
  
      pageMetadata = dataChunk3.getData_chunk_list().get(pageNumber);
  
      if (compressor == null) {
        this.compressor = CompressorFactory.getInstance().getCompressor(
            CarbonMetadataUtil.getCompressorNameFromChunkMeta(pageMetadata.getChunk_meta()));
      }
      // calculating the start point of data
      // as buffer can contain multiple column data, start point will be data chunk offset +
      // data chunk length + page offset
      long offset = dimensionRawColumnChunk.getOffSet() + dimensionChunksLength
          .get(dimensionRawColumnChunk.getColumnIndex()) + dataChunk3.getPage_offset()
          .get(pageNumber);
      int length = pageMetadata.data_page_length;
      if (CarbonUtil.hasEncoding(pageMetadata.encoders, Encoding.INVERTED_INDEX)) {
        length += pageMetadata.rowid_page_length;
      }
  
      if (CarbonUtil.hasEncoding(pageMetadata.encoders, Encoding.RLE)) {
        length += pageMetadata.rle_page_length;
      }
      // get the data buffer
      ByteBuffer rawData = dimensionRawColumnChunk.getFileReader()
          .readByteBuffer(filePath, offset, length);
  
      return decodeDimension(dimensionRawColumnChunk, rawData, pageMetadata, 0, null,
          reusableDataBuffer);
    }
  }