callback-based interface

Martin Pool <[email protected]> Thu, 8 Apr 2004 12:55:55 +1000
Newsgroups gmane.network.librsync.devel
Message-ID <[email protected]>
--Signature=_Thu__8_Apr_2004_12_55_55_+1000_yv/UZCSVG7cPH3=i
Content-Type: multipart/mixed;
 boundary="Multipart=_Thu__8_Apr_2004_12_55_55_+1000_4U5WNa/J7.MM0mnf"


--Multipart=_Thu__8_Apr_2004_12_55_55_+1000_4U5WNa/J7.MM0mnf
Content-Type: text/plain; charset=US-ASCII
Content-Disposition: inline
Content-Transfer-Encoding: 7bit

I have done a bit of work on changing librsync to use only callbacks
to move data in and out of the library, rather than the rs_buffers in
0.9.  

I haven't finished the conversion, and I am not yet sure that this is
the right way to go.  But I am pretty happy with the way this
simplifies the interface, documentation and the implementation.  I am
thinking about switching the interface for a future 0.10 release.

-- 
Martin 

--Multipart=_Thu__8_Apr_2004_12_55_55_+1000_4U5WNa/J7.MM0mnf
Content-Type: text/x-chdr;
 name="librsync.h"
Content-Disposition: attachment;
 filename="librsync.h"
Content-Transfer-Encoding: 7bit

/*=                    -*- c-basic-offset: 4; indent-tabs-mode: nil; -*-
 *
 * librsync -- library for network deltas
 * 
 * Copyright (C) 2000, 2001, 04 by Martin Pool <[email protected]>
 * Copyright (C) 2003 by Donovan Baarda <[email protected]> 
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

                              /*
                               | You should never wear your best
                               | trousers when you go out to fight for
                               | freedom and liberty.
                               |        -- Henrik Ibsen
                               */


/* librsync.h -- Main public interface to librsync.
 */

/* TODO: Perhaps make the trace settings per-job?  But then how would
 * callbacks log their information?  Probably not justified. */

/* TODO: Perhaps add functions to customize the input and output
 * buffer sizes. */

#ifndef _RSYNC_H
#define _RSYNC_H



#ifdef __cplusplus
#  define BEGIN_C extern "C" {
#  define END_C }
#else
#  define BEGIN_C
#  define END_C
#endif

BEGIN_C


/* librsync needs a 64-bit type.  By default we'll rely on the C99
 * stdint.h file.  If there are target systems that don't have that,
 * we might need to try something else. */
#include <sys/types.h>
#include <stdint.h>
typedef uint64_t rs_long_t;



extern char const *rs_librsync_version;
extern char const *rs_licence_string;

struct rs_job;

typedef struct rs_job rs_job_t;


/* Length in bytes of MD4 hash.  This is a good default for the strong
 * sum length. */
#define RS_MD4_LENGTH 16



/**
 * \brief Log severity levels.
 *
 * These are the same as syslog, at least in glibc.
 *
 * \sa rs_trace_set_level()
 */
typedef enum {
    RS_LOG_EMERG         = 0,   /**< System is unusable */
    RS_LOG_ALERT         = 1,   /**< Action must be taken immediately */
    RS_LOG_CRIT          = 2,   /**< Critical conditions */
    RS_LOG_ERR           = 3,   /**< Error conditions */
    RS_LOG_WARNING       = 4,   /**< Warning conditions */
    RS_LOG_NOTICE        = 5,   /**< Normal but significant condition */
    RS_LOG_INFO          = 6,   /**< Informational */
    RS_LOG_DEBUG         = 7    /**< Debug-level messages */
} rs_loglevel;




/**
 * \typedef rs_trace_fn_t
 * \brief Callback to write out log messages.
 * \param level a syslog level.
 * \param msg message to be logged.
 */
typedef void    rs_trace_fn_t(int level, char const *msg);

void            rs_trace_set_level(rs_loglevel level);

/** Set trace callback. */
void            rs_trace_to(rs_trace_fn_t *);

/** Default trace callback that writes to stderr.  Implements
 * ::rs_trace_fn_t, and may be passed to rs_trace_to(). */
void            rs_trace_stderr(int level, char const *msg);

/** Check whether the library was compiled with debugging trace
 * suport. */
int             rs_supports_trace(void);



/**
 * Convert FROM_LEN bytes at FROM_BUF into a hex representation in
 * TO_BUF, which must be twice as long plus one byte for the null
 * terminator.
 */
void     rs_hexify(char *to_buf, void const *from_buf, int from_len);

/**
 * Decode a base64 buffer in place.  \return the number of binary
 * bytes.
 */
size_t rs_unbase64(char *s);


/**
 * Encode a buffer as base64.
 */
void rs_base64(unsigned char const *buf, int n, char *out);


/**
 * \brief Return codes from nonblocking rsync operations.
 */
typedef enum {
    RS_DONE =		0,	/**< Completed successfully. */
    RS_BLOCKED =	1, 	/**< Blocked waiting for more data. */
    RS_RUNNING  =       2,      /**< Not yet finished or blocked.
                                 * This value should never be returned
                                 * to the caller.  */

    /* 77 matches the value used in autoconf tests.  Maybe it has some
     * other significance? */
    RS_TEST_SKIPPED =   77,     /**< Test neither passed or failed. */
    
    RS_IO_ERROR =	100,    /**< Error in file or network IO. */
    RS_SYNTAX_ERROR =   101,    /**< Command line syntax error. */
    RS_MEM_ERROR =	102,    /**< Out of memory. */
    RS_INPUT_ENDED =	103,	/**< End of input file, possibly
                                   unexpected. */
    RS_BAD_MAGIC =      104,    /**< Bad magic number at start of
                                   stream.  Probably not a librsync
                                   file, or possibly the wrong kind of
                                   file or from an incompatible
                                   library version. */
    RS_UNIMPLEMENTED =  105,    /**< Author is lazy. */
    RS_CORRUPT =        106,    /**< Unbelievable value in stream. */
    RS_INTERNAL_ERROR = 107,    /**< Probably a library bug. */
    RS_PARAM_ERROR =    108,     /**< Bad value passed in to library,
                                 * probably an application bug. */
    RS_EOF            = 109
} rs_result;
typedef rs_result rs_result_t;

/* FIXME: Pick either 'rs_result' or 'rs_result_t' */



/* Return a brief English description of an rs_result, such as "IO error". */
char const *rs_strerror(rs_result);


/* Return the symbolic name for a result */
const char *rs_result_str(rs_result); 


/**
 * \brief Performance statistics from a librsync encoding or decoding
 * operation.
 *
 * \sa rs_format_stats(), rs_log_stats()
 */
typedef struct rs_stats {
    char const     *op;     /**< Human-readable name of current
                             * operation.  For example, "delta". */
    int             lit_cmds;   /**< Number of literal commands. */
    rs_long_t       lit_bytes;  /**< Number of literal bytes. */
    rs_long_t       lit_cmdbytes; /**< Number of bytes used in literal
                                   * command headers. */
        
    rs_long_t       copy_cmds, copy_bytes, copy_cmdbytes;
    rs_long_t       sig_cmds, sig_bytes;
    int             false_matches;

    rs_long_t       sig_blocks; /**< Number of blocks described by the
                                   signature. */

    size_t          block_len;

    rs_long_t       in_bytes;   /**< Total bytes read from input. */
    rs_long_t       out_bytes;  /**< Total bytes written to output. */
} rs_stats_t;


int rs_log_stats(rs_stats_t const *stats);


typedef struct rs_signature rs_signature_t;

void rs_free_sumset(rs_signature_t *);
void rs_sumset_dump(rs_signature_t const *);


/* Callback types for doing IO.  These can return RS_DONE on success,
 * RS_BLOCKED if they cannot proceed at the moment, or an error code.
 * If they return an error, the overall operation fails.  If they
 * return RS_BLOCKED, then the operation is suspended and it can be
 * retried later.
 *
 * If the read routines have hit EOF, they return RS_EOF.
 *
 * These callbacks write data into or read data from a buffer provided
 * by librsync.  Obviously they must never write past the end of the
 * buffer or try other such tricks.
 *
 * The callbacks *may* decline to process the whole buffer.  That is,
 * reads can return before completely filling the buffer, and writes
 * need not accept all the output data.  Callbacks ought to make
 * progress if they can, but they do not need to double-copy data into
 * their own buffer.  If they cannot handle it when they're called, it
 * is OK to just leave it there.
 *
 * In general the callback interface is a lot like way Unix IO works
 * on sockets.
 *
 * The first parameter is an opaque pointer for the use of the
 * callback.  Typically this is a pointer to a file descriptor or some
 * similar context.
 */
typedef rs_result rs_cb_read(void *,
                             char *buf,
                             size_t buf_len,
                             size_t *bytes_read);

typedef rs_result rs_cb_pread(void *,
                              char *buf,
                              size_t buf_len,
                              off_t offset,
                              size_t *bytes_read);

typedef rs_result rs_cb_write(void *,
                              const char *buf,
                              size_t buf_len,
                              size_t *bytes_written);


/* The top-level operation: calculate a signature, load a signature,
 * calculate a delta, apply a delta.
 *
 * These functions just set up the job.  To actually do the work, call
 * rs_job_iter.
 *
 * All these take callbacks which they use to do IO.
 */
rs_result rs_sig_begin(rs_job_t **,
                       size_t block_len,
                       size_t strong_sum_len);

rs_result rs_loadsig_begin(rs_job_t **,
                           rs_signature_t **);

rs_result rs_delta_begin(rs_job_t **,
                         const rs_signature_t *);

rs_result rs_patch_begin(rs_job_t **);



/* Set the callbacks to be used for an operation. */
rs_result rs_job_set_input(rs_job_t *,
                           rs_cb_read *, void *);

rs_result rs_job_set_output(rs_job_t *,
                            rs_cb_write *, void *);


/* TODO: Perhaps set the IO routines somewhere else, rather than
 * duplicating them in all these parameters? */


/* TODO: Make an MD4 calculator that works similarly? */
                         

/* The IO callbacks will be called any number of times, until the
 * input returns EOF and everything has been written out.  (Or until
 * an error occurs.)  If the callbacks block, this function will also
 * return RS_BLOCKED.  When you think there will be more input
 * available or more output space (e.g. after select() returns), call
 * rs_job_iter again.
 *
 * When done, call rs_job_free(). */
rs_result_t rs_job_run(rs_job_t *);

const rs_stats_t * rs_job_statistics(rs_job_t *job);

rs_result_t rs_job_free(rs_job_t *);

int             rs_accum_value(rs_job_t *, char *sum, size_t sum_len);


#ifndef RSYNC_NO_STDIO_INTERFACE
/*
 * Buffer sizes for file IO.
 *
 * You probably only need to change these in testing.
 */
extern int rs_inbuflen, rs_outbuflen;

#include <stdio.h>

rs_result rs_mdfour_file(FILE *in_file, char *result);

rs_result rs_sig_file(FILE *old_file, FILE *sig_file,
                      size_t block_len, size_t strong_len, rs_stats_t *); 

rs_result rs_loadsig_file(FILE *, rs_signature_t **, rs_stats_t *);

rs_result rs_file_copy_cb(void *arg, rs_long_t pos, size_t *len, void **buf);

rs_result rs_delta_file(rs_signature_t *, FILE *new_file, FILE *delta_file, rs_stats_t *);

rs_result rs_patch_file(FILE *basis_file, FILE *delta_file, FILE *new_file, rs_stats_t *);

/* IO callbacks using stdio */
rs_cb_read rs_cb_read_stdio;
rs_cb_write rs_cb_write_stdio;
rs_cb_pread rs_cb_pread_stdio;

#endif /* ! RSYNC_NO_STDIO_INTERFACE */



/* ======================================================================
 *
 * Unix-style IO callbacks.
 *
 * These map into calls to read(2), write(2), etc.
 */

rs_cb_read rs_cb_read_uio;
rs_cb_write rs_cb_write_uio;
rs_cb_pread rs_cb_pread_uio;
rs_result rs_uio_open(const char *file, int mode, int *fd_out);

END_C

#endif /* ! _RSYNC_H */

--Multipart=_Thu__8_Apr_2004_12_55_55_+1000_4U5WNa/J7.MM0mnf--

--Signature=_Thu__8_Apr_2004_12_55_55_+1000_yv/UZCSVG7cPH3=i
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)

iD8DBQFAdL87PGPKP6Cz6IsRAhaxAJ0eT+zSF9/56ffzo6W3d5uDYewv2ACgyI/W
NhWCAY8FWb7tN/sTEf0KYu0=
=/bBD
-----END PGP SIGNATURE-----

--Signature=_Thu__8_Apr_2004_12_55_55_+1000_yv/UZCSVG7cPH3=i--


-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click