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.cassandra.db;
  
  import java.io.IOException;
  
  import org.apache.cassandra.schema.TableMetadata;
  import org.apache.cassandra.db.rows.*;
  import org.apache.cassandra.io.util.DataInputPlus;
  
  /**
   * Helper class to deserialize Unfiltered object from disk efficiently.
   *
   * More precisely, this class is used by the low-level reader to ensure
   * we don't do more work than necessary (i.e. we don't allocate/deserialize
   * objects for things we don't care about).
   */
  public class UnfilteredDeserializer
  {
      protected final TableMetadata metadata;
      protected final DataInputPlus in;
      protected final DeserializationHelper helper;
  
      private final ClusteringPrefix.Deserializer clusteringDeserializer;
      private final SerializationHeader header;
  
      private int nextFlags;
      private int nextExtendedFlags;
      private boolean isReady;
      private boolean isDone;
  
      private final Row.Builder builder;
  
      private UnfilteredDeserializer(TableMetadata metadata,
                                     DataInputPlus in,
                                     SerializationHeader header,
                                     DeserializationHelper helper)
      {
          this.metadata = metadata;
          this.in = in;
          this.helper = helper;
          this.header = header;
          this.clusteringDeserializer = new ClusteringPrefix.Deserializer(metadata.comparator, in, header);
          this.builder = BTreeRow.sortedBuilder();
      }
  
      public static UnfilteredDeserializer create(TableMetadata metadata,
                                                  DataInputPlus in,
                                                  SerializationHeader header,
                                                  DeserializationHelper helper)
      {
          return new UnfilteredDeserializer(metadata, in, header, helper);
      }
  
      /**
       * Whether or not there is more atom to read.
       */
      public boolean hasNext() throws IOException
      {
          if (isReady)
              return true;
  
          prepareNext();
          return !isDone;
      }
  
      private void prepareNext() throws IOException
      {
          if (isDone)
              return;
  
          nextFlags = in.readUnsignedByte();
          if (UnfilteredSerializer.isEndOfPartition(nextFlags))
          {
              isDone = true;
              isReady = false;
              return;
          }
  
          nextExtendedFlags = UnfilteredSerializer.readExtendedFlags(in, nextFlags);
  
          clusteringDeserializer.prepare(nextFlags, nextExtendedFlags);
          isReady = true;
      }
  
      /**
       * Compare the provided bound to the next atom to read on disk.
       *
       * This will not read/deserialize the whole atom but only what is necessary for the
       * comparison. Whenever we know what to do with this atom (read it or skip it),
       * readNext or skipNext should be called.
       */
      public int compareNextTo(ClusteringBound<?> bound) throws IOException
      {
          if (!isReady)
              prepareNext();
  
          assert !isDone;
  
          return clusteringDeserializer.compareNextTo(bound);
      }
  
      /**
       * Returns whether the next atom is a row or not.
       */
      public boolean nextIsRow() throws IOException
      {
          if (!isReady)
              prepareNext();
  
          return UnfilteredSerializer.kind(nextFlags) == Unfiltered.Kind.ROW;
      }
  
      /**
       * Returns the next atom.
       */
      public Unfiltered readNext() throws IOException
      {
          isReady = false;
          if (UnfilteredSerializer.kind(nextFlags) == Unfiltered.Kind.RANGE_TOMBSTONE_MARKER)
          {
              ClusteringBoundOrBoundary<byte[]> bound = clusteringDeserializer.deserializeNextBound();
              return UnfilteredSerializer.serializer.deserializeMarkerBody(in, header, bound);
          }
          else
          {
              builder.newRow(clusteringDeserializer.deserializeNextClustering());
              return UnfilteredSerializer.serializer.deserializeRowBody(in, header, helper, nextFlags, nextExtendedFlags, builder);
          }
      }
  
      /**
       * Clears any state in this deserializer.
       */
      public void clearState()
      {
          isReady = false;
          isDone = false;
      }
  
      /**
       * Skips the next atom.
       */
      public void skipNext() throws IOException
      {
          isReady = false;
          clusteringDeserializer.skipNext();
          if (UnfilteredSerializer.kind(nextFlags) == Unfiltered.Kind.RANGE_TOMBSTONE_MARKER)
          {
              UnfilteredSerializer.serializer.skipMarkerBody(in);
          }
          else
          {
              UnfilteredSerializer.serializer.skipRowBody(in);
          }
      }
  }
  