/*
 * Copyright (c) 2018 Red Hat, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA. 
 *
 * $Id: //eng/uds-releases/flanders/src/public/uds-block.h#10 $
 */

/**
 * @file
 * @brief Definitions for the UDS block interface
 **/
#ifndef UDS_BLOCK_H
#define UDS_BLOCK_H

#include "uds.h"

/** General UDS block constants. */
enum {
  /** The maximum metadata size for a block. */
  UDS_MAX_BLOCK_DATA_SIZE = UDS_MAX_METADATA_SIZE
};

/**
 * A UDS block context.
 **/
typedef struct udsBlockContext {
  /** The context ID. */
  unsigned int id;
} UdsBlockContext;

/**
 * Metadata to associate with a blockName.
 **/
struct udsChunkData {
  unsigned char data[UDS_MAX_BLOCK_DATA_SIZE];
};

/**
 * Represents a block address on disk.
 *
 * #UdsBlockAddress objects allow the Application Software and UDS
 * to refer to specific disk blocks.  It might be, for instance, the
 * logical block address divided by the block size.
 *
 * These objects are stored persistently in the index and are also cached.
 * Therefore, make every effort to ensure that these objects are as small as
 * possible.
 **/
typedef void *UdsBlockAddress;

/**
 * Callback function invoked to inform the Application Software that an
 * operation completed.
 *
 * The <code>context</code>, <code>type</code>, <code>status</code>,
 * <code>cookie</code>, and <code>callbackArgument</code> parameters
 * are always set.  The rest of them depend on the callback type.
 *
 * <ul>
 * <li> <code>duplicateAddress</code>: is set except for #UDS_DELETE.</li>
 * <li> <code>canonicalAddress</code>: is set if the chunk existed.</li>
 * <li> <code>blockName</code>: is always set.</li>
 * <li> <code>blockLength</code>: is zero</li>
 * </ul>
 *
 * On a duplicate block callback, retain the canonical address and adjust the
 * duplicate address to share data with the canonical address.
 *
 * All callbacks are invoked in one thread, so the callback function should
 * not block.  In addition, this function should not call exit() or make
 * additional calls into the index.
 *
 * @param [in,out] context      The context
 * @param [in] type             The callback type
 * @param [in] status           The request status (either
 *                              #UDS_SUCCESS or an error code)
 * @param [in] cookie           The opaque data from the call that invoked
 *                              this callback
 * @param [in] duplicateAddress The duplicate address, which can possibly be
 *                              freed
 * @param [in] canonicalAddress The canonical address of the chunk
 * @param [in] blockName        The name (hash) of the chunk being referenced,
 *                              used for unmapping or remapping this block
 * @param [in] callbackArgument The <code>callbackArgument</code> given when
 *                              registering the callback
 **/
typedef void (*UdsDedupeBlockCallback)(UdsBlockContext  context,
                                       UdsCallbackType  type,
                                       int              status,
                                       UdsCookie        cookie,
                                       UdsBlockAddress  duplicateAddress,
                                       UdsBlockAddress  canonicalAddress,
                                       UdsChunkName    *blockName,
                                       void            *callbackArgument);

/** @{ */
/** @name Context Management */

/**
 * Opens a new block context for an index session.
 *
 * You must first create the index session with #udsCreateLocalIndex,
 * #udsLoadLocalIndex, or #udsRebuildLocalIndex.
 *
 * The Application Software can use this context for any number of operations
 * while the underlying index session is active. Call #udsCloseBlockContext to
 * close the context.
 *
 * If a fatal error occurs on either the context or the underlying index
 * session, or if the index session has been closed, all subsequent operations
 * will return #UDS_DISABLED.  In that case, close the block context, close the
 * index session, and then reload or rebuild the index. Then call
 * #udsOpenBlockContext to open a fresh block context.
 *
 * @param [in] session       The index session
 * @param [in] metadataSize  The size of metadata to be passed using the
 *                           context
 * @param [out] context      The new block context
 *
 * @return Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsOpenBlockContext(UdsIndexSession  session,
                        unsigned int     metadataSize,
                        UdsBlockContext *context);

/**
 * Closes a block context.
 *
 * #udsCloseBlockContext flushes and saves work in that context
 * before closing it.
 *
 * @param [in] context  The block context to close
 *
 * @return              Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsCloseBlockContext(UdsBlockContext context);

/**
 * Waits until all callbacks for index operations are complete.
 *
 * @param [in] context  The block context to flush
 *
 * @return              Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsFlushBlockContext(UdsBlockContext context);

/**
 * Registers a callback to be invoked when asynchronous index operations are
 * complete.
 *
 * The #udsPostBlockName function invokes the registered callback to inform the
 * Application Software that an operation is complete.
 *
 * @param [in] context          The library context
 * @param [in] cb               The callback function, or <code>NULL</code>
 *                              to unregister an existing callback
 * @param [in] callbackArgument Opaque argument for the callback
 *
 * @return                      Either #UDS_SUCCESS or an error code
 *
 * @see #UdsDedupeBlockCallback
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsRegisterDedupeBlockCallback(UdsBlockContext         context,
                                   UdsDedupeBlockCallback  cb,
                                   void                   *callbackArgument);

/**
 * Controls the maximum size of the backlog of pending requests
 * managed within the UDS library.
 *
 * Higher values allow for more parallelism and better amortize
 * processing time (especially when the fingerprint API is not used),
 * but also tends to increase turnaround time of individual requests.
 *
 * @param [in] context          The library context
 * @param [in] maxRequests      The maximum backlog size
 *
 * @return                      Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsSetBlockContextRequestQueueLimit(UdsBlockContext context,
                                        unsigned int    maxRequests);
/** @} */

/** @{ */
/** @name Deduplication */

/**
 * Indexes a block name and asynchronously associates it with a particular
 * address;  if a callback function has been registered, it is invoked upon
 * completion.
 *
 * This is an asynchronous interface to the block-oriented UDS
 * API.  The Application Software provides the block's name. UDS then
 * checks this name against its index.
 * <ul>
 *   <li>If the block is new, it is stored in the index.</li>
 *   <li>If the block is a duplicate of an indexed block, UDS returns the
 *       canonical block address via the #UdsDedupeBlockCallback callback.</li>
 * </ul>
 *
 * @param [in] context      The context for the block
 * @param [in] cookie       Opaque data for the callback
 * @param [in] blockAddress The address of the block being referenced
 * @param [in] chunkName    The name of the block
 *
 * @return                  Either #UDS_SUCCESS or an error code
 *
 * @see #udsRegisterDedupeBlockCallback
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsPostBlockName(UdsBlockContext     context,
                     UdsCookie           cookie,
                     UdsBlockAddress     blockAddress,
                     const UdsChunkName *chunkName);

typedef struct udsRequest UdsRequest;

/**
 * Callback function invoked to inform the Application Software that an
 * operation started by #udsStartChunkOperation has completed.
 *
 * @param [in] request  The operation that finished.  When the callback
 *                      function is called, this UdsRequest structure can be
 *                      reused or freed.
 **/
typedef void UdsChunkCallback(UdsRequest *request);

/**
 * Request structure passed to #udsStartChunkOperation to begin an operation,
 * and returned to the Application Software when the callback function is
 * invoked.
 **/
struct udsRequest {
  /*
   * The name of the block.
   * Set before starting an operation.
   * Unchanged at time of callback.
   */
  UdsChunkName chunkName;
  /*
   * The metadata found in the index that was associated with the block
   * (sometimes called the canonical address).
   * Set before the callback.
   */
  struct udsChunkData oldMetadata;
  /*
   * The new metadata to associate with the name of the block (sometimes called
   * the duplicate address).
   * Set before starting a #UDS_POST or #UDS_QUERY operation.
   * Unchanged at time of callback.
   */
  struct udsChunkData newMetadata;
  /*
   * The callback method to be invoked when the operation finishes.
   * Set before starting an operation.
   * Unchanged at time of callback.
   */
  UdsChunkCallback *callback;
  /*
   * The block context.
   * Set before starting an operation.
   * Unchanged at time of callback.
   */
  UdsBlockContext context;
  /*
   * The operation type, which is one of #UDS_DELETE, #UDS_POST, #UDS_QUERY or
   * #UDS_UPDATE.
   * Set before starting an operation.
   * Unchanged at time of callback.
   */
  UdsCallbackType type;
  /*
   * The operation status, which is either #UDS_SUCCESS or an error code.
   * Set before the callback.
   */
  int status;
  /*
   * If true, the name of the block was found in the index.
   * Set before the callback.
   */
  bool found;
  /*
   * If true, move the entry to the end of the deduplication window.
   * Set before starting a #UDS_QUERY operation.
   * Unchanged at time of callback.
   */
  bool update;
  long private[25];
};

/**
 * Start a UDS index chunk operation.  The request <code>type</code> field must
 * be set to the type of operation.  This is an asynchronous interface to the
 * block-oriented UDS API.  The callback is invoked upon completion.
 *
 * The #UDS_DELETE operation type deletes the mapping for a particular block.
 * #UDS_DELETE is typically used when UDS provides invalid advice.
 *
 * The #UDS_POST operation type indexes a block name and associates it with a
 * particular address.  The caller provides the block's name. UDS then checks
 * this name against its index.
 * <ul>
 *   <li>If the block is new, it is stored in the index.</li>
 *   <li>If the block is a duplicate of an indexed block, UDS returns the
 *       canonical block address via the callback.</li>
 * </ul>
 *
 * The #UDS_QUERY operation type checks to see if a block name exists in the
 * index.  The caller provides the block's name.  UDS then checks
 * this name against its index.
 * <ul>
 *   <li>If the block is new, no action is taken.</li>

 *   <li>If the block is a duplicate of an indexed block, UDS returns the
 *       canonical block address via the callback.  If the <code>update</code>
 *       field is set, the entry is moved to the end of the deduplication
 *       window.</li> </ul>
 *
 * The #UDS_UPDATE operation type updates the mapping for a particular block.
 * #UDS_UPDATE is typically used if the callback function provides invalid
 * advice.
 *
 * @param [in] request  The operation.  The <code>type</code>,
 *                      <code>chunkName</code>, <code>newMetadata</code>,
 *                      <code>context</code>, <code>callback</code>, and
 *                      <code>update</code> fields must be set.  At callback
 *                      time, the <code>oldMetadata</code>,
 *                      <code>status</code>, and <code>found</code> fields will
 *                      be set.
 *
 * @return              Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsStartChunkOperation(UdsRequest *request);
/** @} */

/** @{ */
/** @name Monitoring */

/**
 * Returns the configuration for the given context.
 *
 * @param [in]  context The block context
 * @param [out] conf    The index configuration
 *
 * @return              Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsGetBlockContextConfiguration(UdsBlockContext   context,
                                    UdsConfiguration *conf);

/**
 * Fetches index statistics for the given context.
 *
 * @param [in]  context The block context
 * @param [out] stats   The index statistics structure to fill
 *
 * @return              Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsGetBlockContextIndexStats(UdsBlockContext  context,
                                 UdsIndexStats   *stats);

/**
 * Fetches context statistics for the given context.
 *
 * @param [in]  context The block context
 * @param [out] stats   The context statistics structure to fill
 *
 * @return              Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsGetBlockContextStats(UdsBlockContext  context,
                            UdsContextStats *stats);

/**
 * Resets context statistics for the given context.
 *
 * @param [in] context The block context to reset
 *
 * @return             Either #UDS_SUCCESS or an error code
 **/
UDS_ATTR_WARN_UNUSED_RESULT
int udsResetBlockContextStats(UdsBlockContext context);

/** @} */

#endif /* UDS_BLOCK_H */
