/* dag.h : DAG-like interface filesystem, private to libsvn_fs
 *
 * ====================================================================
 *    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.
 * ====================================================================
 */

#ifndef SVN_LIBSVN_FS_DAG_H
#define SVN_LIBSVN_FS_DAG_H

#include "svn_fs.h"
#include "svn_delta.h"
#include "private/svn_cache.h"

#include "fs.h"
#include "id.h"

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


/* The interface in this file provides all the essential filesystem
   operations, but exposes the filesystem's DAG structure.  This makes
   it simpler to implement than the public interface, since a client
   of this interface has to understand and cope with shared structure
   directly as it appears in the database.  However, it's still a
   self-consistent set of invariants to maintain, making it
   (hopefully) a useful interface boundary.

   In other words:

   - The dag_node_t interface exposes the internal DAG structure of
     the filesystem, while the svn_fs.h interface does any cloning
     necessary to make the filesystem look like a tree.

   - The dag_node_t interface exposes the existence of copy nodes,
     whereas the svn_fs.h handles them transparently.

   - dag_node_t's must be explicitly cloned, whereas the svn_fs.h
     operations make clones implicitly.

   - Callers of the dag_node_t interface use Berkeley DB transactions
     to ensure consistency between operations, while callers of the
     svn_fs.h interface use Subversion transactions.  */


/* Generic DAG node stuff.  */

typedef struct dag_node_t dag_node_t;

/* Fill *NODE with a dag_node_t representing node revision ID in FS,
   allocating in RESULT_POOL.  Use SCRATCH_POOL for temporaries. */
svn_error_t *
svn_fs_x__dag_get_node(dag_node_t **node,
                       svn_fs_t *fs,
                       const svn_fs_x__id_t *id,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool);


/* Return a new dag_node_t object referring to the same node as NODE,
   allocated in RESULT_POOL.  If you're trying to build a structure in a
   pool that wants to refer to dag nodes that may have been allocated
   elsewhere, you can call this function and avoid inter-pool pointers. */
dag_node_t *
svn_fs_x__dag_dup(const dag_node_t *node,
                  apr_pool_t *result_pool);

/* If NODE has been allocated in POOL, return NODE.  Otherwise, return
   a copy created in RESULT_POOL with svn_fs_fs__dag_dup. */
dag_node_t *
svn_fs_x__dag_copy_into_pool(dag_node_t *node,
                             apr_pool_t *result_pool);

/* Serialize a DAG node, except don't try to preserve the 'fs' member.
   Implements svn_cache__serialize_func_t */
svn_error_t *
svn_fs_x__dag_serialize(void **data,
                        apr_size_t *data_len,
                        void *in,
                        apr_pool_t *pool);

/* Deserialize a DAG node, leaving the 'fs' member as NULL.
   Implements svn_cache__deserialize_func_t */
svn_error_t *
svn_fs_x__dag_deserialize(void **out,
                          void *data,
                          apr_size_t data_len,
                          apr_pool_t *pool);

/* Return the filesystem containing NODE.  */
svn_fs_t *
svn_fs_x__dag_get_fs(dag_node_t *node);

/* Changes the filesystem containing NODE to FS.  (Used when pulling
   nodes out of a shared cache, say.) */
void
svn_fs_x__dag_set_fs(dag_node_t *node,
                     svn_fs_t *fs);


/* Return NODE's revision number.  If NODE has never been committed as
   part of a revision, set *REV to SVN_INVALID_REVNUM.  */
svn_revnum_t
svn_fs_x__dag_get_revision(const dag_node_t *node);


/* Return the node revision ID of NODE.  The value returned is shared
   with NODE, and will be deallocated when NODE is.  */
const svn_fs_x__id_t *
svn_fs_x__dag_get_id(const dag_node_t *node);

/* Return the node ID of NODE.  The value returned is shared with NODE,
   and will be deallocated when NODE is.  */
svn_error_t *
svn_fs_x__dag_get_node_id(svn_fs_x__id_t *node_id,
                          dag_node_t *node);

/* Return the copy ID of NODE.  The value returned is shared with NODE,
   and will be deallocated when NODE is.  */
svn_error_t *
svn_fs_x__dag_get_copy_id(svn_fs_x__id_t *copy_id,
                          dag_node_t *node);

/* Set *SAME to TRUE, if nodes LHS and RHS have the same node ID. */
svn_error_t *
svn_fs_x__dag_related_node(svn_boolean_t *same,
                           dag_node_t *lhs,
                           dag_node_t *rhs);

/* Set *SAME to TRUE, if nodes LHS and RHS have the same node and copy IDs.
 */
svn_error_t *
svn_fs_x__dag_same_line_of_history(svn_boolean_t *same,
                                   dag_node_t *lhs,
                                   dag_node_t *rhs);

/* Return the created path of NODE.  The value returned is shared
   with NODE, and will be deallocated when NODE is.  */
const char *
svn_fs_x__dag_get_created_path(dag_node_t *node);


/* Set *ID_P to the node revision ID of NODE's immediate predecessor.
 */
svn_error_t *
svn_fs_x__dag_get_predecessor_id(svn_fs_x__id_t *id_p,
                                 dag_node_t *node);


/* Set *COUNT to the number of predecessors NODE has (recursively).
 */
/* ### This function is currently only used by 'verify'. */
svn_error_t *
svn_fs_x__dag_get_predecessor_count(int *count,
                                    dag_node_t *node);

/* Set *COUNT to the number of node under NODE (inclusive) with
   svn:mergeinfo properties.
 */
svn_error_t *
svn_fs_x__dag_get_mergeinfo_count(apr_int64_t *count,
                                  dag_node_t *node);

/* Set *DO_THEY to a flag indicating whether or not NODE is a
   directory with at least one descendant (not including itself) with
   svn:mergeinfo.
 */
svn_error_t *
svn_fs_x__dag_has_descendants_with_mergeinfo(svn_boolean_t *do_they,
                                             dag_node_t *node);

/* Set *HAS_MERGEINFO to a flag indicating whether or not NODE itself
   has svn:mergeinfo set on it.
 */
svn_error_t *
svn_fs_x__dag_has_mergeinfo(svn_boolean_t *has_mergeinfo,
                            dag_node_t *node);

/* Return non-zero IFF NODE is currently mutable. */
svn_boolean_t
svn_fs_x__dag_check_mutable(const dag_node_t *node);

/* Return the node kind of NODE. */
svn_node_kind_t
svn_fs_x__dag_node_kind(dag_node_t *node);

/* Set *PROPLIST_P to a PROPLIST hash representing the entire property
   list of NODE, allocating from POOL.  The hash has const char *
   names (the property names) and svn_string_t * values (the property
   values).

   If properties do not exist on NODE, *PROPLIST_P will be set to
   NULL.

   Allocate the result in RESULT_POOL and use SCRATCH_POOL for temporaries.
 */
svn_error_t *
svn_fs_x__dag_get_proplist(apr_hash_t **proplist_p,
                           dag_node_t *node,
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool);

/* Set the property list of NODE to PROPLIST, allocating from POOL.
   The node being changed must be mutable.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_set_proplist(dag_node_t *node,
                           apr_hash_t *proplist,
                           apr_pool_t *scratch_pool);

/* Increment the mergeinfo_count field on NODE by INCREMENT.  The node
   being changed must be mutable.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_increment_mergeinfo_count(dag_node_t *node,
                                        apr_int64_t increment,
                                        apr_pool_t *scratch_pool);

/* Set the has-mergeinfo flag on NODE to HAS_MERGEINFO.  The node
   being changed must be mutable.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_set_has_mergeinfo(dag_node_t *node,
                                svn_boolean_t has_mergeinfo,
                                apr_pool_t *scratch_pool);



/* Revision and transaction roots.  */


/* Open the root of revision REV of filesystem FS, allocating from
   RESULT_POOL.  Set *NODE_P to the new node.  Use SCRATCH_POOL for
   temporary allocations.*/
svn_error_t *
svn_fs_x__dag_revision_root(dag_node_t **node_p,
                            svn_fs_t *fs,
                            svn_revnum_t rev,
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool);


/* Set *NODE_P to the root of transaction TXN_ID in FS, allocating
   from RESULT_POOL.  Use SCRATCH_POOL for temporary allocations. */
svn_error_t *
svn_fs_x__dag_txn_root(dag_node_t **node_p,
                       svn_fs_t *fs,
                       svn_fs_x__txn_id_t txn_id,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool);


/* Directories.  */


/* Open the node named NAME in the directory PARENT.  Set *CHILD_P to
   the new node, allocated in RESULT_POOL.  NAME must be a single path
   component; it cannot be a slash-separated directory path.  If NAME does
   not exist within PARENT, set *CHILD_P to NULL.
 */
svn_error_t *
svn_fs_x__dag_open(dag_node_t **child_p,
                   dag_node_t *parent,
                   const char *name,
                   apr_pool_t *result_pool,
                   apr_pool_t *scratch_pool);


/* Set *ENTRIES_P to an array of NODE's entries, sorted by entry names,
   and the values are svn_fs_x__dirent_t. The returned table (and elements)
   is allocated in RESULT_POOL, temporaries in SCRATCH_POOL. */
svn_error_t *
svn_fs_x__dag_dir_entries(apr_array_header_t **entries_p,
                          dag_node_t *node,
                          apr_pool_t *result_pool,
                          apr_pool_t *scratch_pool);

/* Set ENTRY_NAME in NODE to point to ID (with kind KIND), allocating
   from POOL.  NODE must be a mutable directory.  ID can refer to a
   mutable or immutable node.  If ENTRY_NAME does not exist, it will
   be created.  TXN_ID is the Subversion transaction under which this
   occurs.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_set_entry(dag_node_t *node,
                        const char *entry_name,
                        const svn_fs_x__id_t *id,
                        svn_node_kind_t kind,
                        svn_fs_x__txn_id_t txn_id,
                        apr_pool_t *scratch_pool);


/* Make a new mutable clone of the node named NAME in PARENT, and
   adjust PARENT's directory entry to point to it, unless NAME in
   PARENT already refers to a mutable node.  In either case, set
   *CHILD_P to a reference to the new node, allocated in POOL.  PARENT
   must be mutable.  NAME must be a single path component; it cannot
   be a slash-separated directory path.  PARENT_PATH must be the
   canonicalized absolute path of the parent directory.

   COPY_ID, if non-NULL, is a key into the `copies' table, and
   indicates that this new node is being created as the result of a
   copy operation, and specifically which operation that was.

   PATH is the canonicalized absolute path at which this node is being
   created.

   TXN_ID is the Subversion transaction under which this occurs.

   Allocate *CHILD_P in RESULT_POOL and use SCRATCH_POOL for temporaries.
 */
svn_error_t *
svn_fs_x__dag_clone_child(dag_node_t **child_p,
                          dag_node_t *parent,
                          const char *parent_path,
                          const char *name,
                          const svn_fs_x__id_t *copy_id,
                          svn_fs_x__txn_id_t txn_id,
                          svn_boolean_t is_parent_copyroot,
                          apr_pool_t *result_pool,
                          apr_pool_t *scratch_pool);


/* Delete the directory entry named NAME from PARENT, allocating from
   POOL.  PARENT must be mutable.  NAME must be a single path
   component; it cannot be a slash-separated directory path.  If the
   node being deleted is a mutable directory, remove all mutable nodes
   reachable from it.  TXN_ID is the Subversion transaction under
   which this occurs.

   If return SVN_ERR_FS_NO_SUCH_ENTRY, then there is no entry NAME in
   PARENT.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_delete(dag_node_t *parent,
                     const char *name,
                     svn_fs_x__txn_id_t txn_id,
                     apr_pool_t *scratch_pool);


/* Create a new mutable directory named NAME in PARENT.  Set *CHILD_P
   to a reference to the new node, allocated in RESULT_POOL.  The new
   directory has no contents, and no properties.  PARENT must be
   mutable.  NAME must be a single path component; it cannot be a
   slash-separated directory path.  PARENT_PATH must be the
   canonicalized absolute path of the parent directory.  PARENT must
   not currently have an entry named NAME.  TXN_ID is the Subversion
   transaction under which this occurs.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_make_dir(dag_node_t **child_p,
                       dag_node_t *parent,
                       const char *parent_path,
                       const char *name,
                       svn_fs_x__txn_id_t txn_id,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool);



/* Files.  */


/* Set *CONTENTS to a readable generic stream which yields the
   contents of FILE.  Allocate the stream in RESULT_POOL.

   If FILE is not a file, return SVN_ERR_FS_NOT_FILE.
 */
svn_error_t *
svn_fs_x__dag_get_contents(svn_stream_t **contents,
                           dag_node_t *file,
                           apr_pool_t *result_pool);

/* Attempt to fetch the contents of NODE and pass it along with the BATON
   to the PROCESSOR.   Set *SUCCESS only of the data could be provided
   and the processor had been called.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_try_process_file_contents(svn_boolean_t *success,
                                        dag_node_t *node,
                                        svn_fs_process_contents_func_t processor,
                                        void* baton,
                                        apr_pool_t *scratch_pool);


/* Set *STREAM_P to a delta stream that will turn the contents of SOURCE into
   the contents of TARGET, allocated in RESULT_POOL.  If SOURCE is null, the
   empty string will be used is its stead.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_get_file_delta_stream(svn_txdelta_stream_t **stream_p,
                                    dag_node_t *source,
                                    dag_node_t *target,
                                    apr_pool_t *result_pool,
                                    apr_pool_t *scratch_pool);

/* Return a generic writable stream in *CONTENTS with which to set the
   contents of FILE.  Allocate the stream in RESULT_POOL.

   Any previous edits on the file will be deleted, and a new edit
   stream will be constructed.
 */
svn_error_t *
svn_fs_x__dag_get_edit_stream(svn_stream_t **contents,
                              dag_node_t *file,
                              apr_pool_t *result_pool);


/* Signify the completion of edits to FILE made using the stream
   returned by svn_fs_x__dag_get_edit_stream.

   If CHECKSUM is non-null, it must match the checksum for FILE's
   contents (note: this is not recalculated, the recorded checksum is
   used), else the error SVN_ERR_CHECKSUM_MISMATCH is returned.

   This operation is a no-op if no edits are present.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_finalize_edits(dag_node_t *file,
                             const svn_checksum_t *checksum,
                             apr_pool_t *scratch_pool);


/* Set *LENGTH to the length of the contents of FILE.
 */
svn_error_t *
svn_fs_x__dag_file_length(svn_filesize_t *length,
                          dag_node_t *file);

/* Put the recorded checksum of type KIND for FILE into CHECKSUM, allocating
   from RESULT_POOL.

   If no stored checksum is available, do not calculate the checksum,
   just put NULL into CHECKSUM.
 */
svn_error_t *
svn_fs_x__dag_file_checksum(svn_checksum_t **checksum,
                            dag_node_t *file,
                            svn_checksum_kind_t kind,
                            apr_pool_t *result_pool);

/* Create a new mutable file named NAME in PARENT.  Set *CHILD_P to a
   reference to the new node, allocated in RESULT_POOL.  The new file's
   contents are the empty string, and it has no properties.  PARENT
   must be mutable.  NAME must be a single path component; it cannot
   be a slash-separated directory path.  PARENT_PATH must be the
   canonicalized absolute path of the parent directory.  TXN_ID is the
   Subversion transaction under which this occurs.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_make_file(dag_node_t **child_p,
                        dag_node_t *parent,
                        const char *parent_path,
                        const char *name,
                        svn_fs_x__txn_id_t txn_id,
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool);



/* Copies */

/* Make ENTRY in TO_NODE be a copy of FROM_NODE.  TO_NODE must be mutable.
   TXN_ID is the Subversion transaction under which this occurs.

   If PRESERVE_HISTORY is true, the new node will record that it was
   copied from FROM_PATH in FROM_REV; therefore, FROM_NODE should be
   the node found at FROM_PATH in FROM_REV, although this is not
   checked.  FROM_PATH should be canonicalized before being passed
   here.

   If PRESERVE_HISTORY is false, FROM_PATH and FROM_REV are ignored.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_copy(dag_node_t *to_node,
                   const char *entry,
                   dag_node_t *from_node,
                   svn_boolean_t preserve_history,
                   svn_revnum_t from_rev,
                   const char *from_path,
                   svn_fs_x__txn_id_t txn_id,
                   apr_pool_t *scratch_pool);


/* Comparison */

/* Find out what is the same between two nodes.  If STRICT is FALSE,
   this function may report false positives, i.e. report changes even
   if the resulting contents / props are equal.

   If PROPS_CHANGED is non-null, set *PROPS_CHANGED to 1 if the two
   nodes have different property lists, or to 0 if same.

   If CONTENTS_CHANGED is non-null, set *CONTENTS_CHANGED to 1 if the
   two nodes have different contents, or to 0 if same.  NODE1 and NODE2
   must refer to files from the same filesystem.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_things_different(svn_boolean_t *props_changed,
                               svn_boolean_t *contents_changed,
                               dag_node_t *node1,
                               dag_node_t *node2,
                               svn_boolean_t strict,
                               apr_pool_t *scratch_pool);


/* Set *REV and *PATH to the copyroot revision and path of node NODE, or
   to SVN_INVALID_REVNUM and NULL if no copyroot exists.
 */
svn_error_t *
svn_fs_x__dag_get_copyroot(svn_revnum_t *rev,
                           const char **path,
                           dag_node_t *node);

/* Set *REV to the copyfrom revision associated with NODE.
 */
svn_error_t *
svn_fs_x__dag_get_copyfrom_rev(svn_revnum_t *rev,
                               dag_node_t *node);

/* Set *PATH to the copyfrom path associated with NODE.
 */
svn_error_t *
svn_fs_x__dag_get_copyfrom_path(const char **path,
                                dag_node_t *node);

/* Update *TARGET so that SOURCE is it's predecessor.

   Use SCRATCH_POOL for temporary allocations.
 */
svn_error_t *
svn_fs_x__dag_update_ancestry(dag_node_t *target,
                              dag_node_t *source,
                              apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* SVN_LIBSVN_FS_DAG_H */
