⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 reps-strings.c

📁 linux subdivision ying gai ke yi le ba
💻 C
📖 第 1 页 / 共 4 页
字号:
}


struct read_rep_args
{
  struct rep_read_baton *rb;   /* The data source.             */
  char *buf;                   /* Where to put what we read.   */
  apr_size_t *len;             /* How much to read / was read. */
};


/* BATON is of type `read_rep_args':

   Read into BATON->rb->buf the *(BATON->len) bytes starting at
   BATON->rb->offset from the data represented at BATON->rb->rep_key
   in BATON->rb->fs, as part of TRAIL.

   Afterwards, *(BATON->len) is the number of bytes actually read, and
   BATON->rb->offset is incremented by that amount.

   If BATON->rb->rep_key is null, this is assumed to mean the file's
   contents have no representation, i.e., the file has no contents.
   In that case, if BATON->rb->offset > 0, return the error
   SVN_ERR_FS_FILE_CONTENTS_CHANGED, else just set *(BATON->len) to
   zero and return.  */
static svn_error_t *
txn_body_read_rep (void *baton, trail_t *trail)
{
  struct read_rep_args *args = baton;

  if (args->rb->rep_key)
    {
      SVN_ERR (rep_read_range (args->rb->fs,
                               args->rb->rep_key,
                               args->rb->offset,
                               args->buf,
                               args->len,
                               trail));

      args->rb->offset += *(args->len);

      /* We calculate the checksum just once, the moment we see the
       * last byte of data.  But we can't assume there was a short
       * read.  The caller may have known the length of the data and
       * requested exactly that amount, so there would never be a
       * short read.  (That's why the read baton has to know the
       * length of the data in advance.)
       *
       * On the other hand, some callers invoke the stream reader in a
       * loop whose termination condition is that the read returned
       * zero bytes of data -- which usually results in the read
       * function being called one more time *after* the call that got
       * a short read (indicating end-of-stream).
       *
       * The conditions below ensure that we compare checksums even
       * when there is no short read associated with the last byte of
       * data, while also ensuring that it's harmless to repeatedly
       * read 0 bytes from the stream.
       */
      if (! args->rb->checksum_finalized)
        {
          apr_md5_update (&(args->rb->md5_context), args->buf, *(args->len));

          if (args->rb->offset == args->rb->size)
            {
              representation_t *rep;
              unsigned char checksum[APR_MD5_DIGESTSIZE];

              apr_md5_final (checksum, &(args->rb->md5_context));
              args->rb->checksum_finalized = TRUE;

              SVN_ERR (svn_fs_bdb__read_rep (&rep, args->rb->fs,
                                             args->rb->rep_key, trail));
              if (! svn_md5_digests_match (checksum, rep->checksum))
                return svn_error_createf
                  (SVN_ERR_FS_CORRUPT, NULL,
                   "Checksum mismatch on rep '%s':\n"
                   "   expected:  %s\n"
                   "     actual:  %s\n", args->rb->rep_key,
                   svn_md5_digest_to_cstring (rep->checksum, trail->pool),
                   svn_md5_digest_to_cstring (checksum, trail->pool));
            }
        }
    }
  else if (args->rb->offset > 0)
    {
      return
        svn_error_create
        (SVN_ERR_FS_REP_CHANGED, NULL,
         "Null rep, but offset past zero already");
    }
  else
    *(args->len) = 0;

  return SVN_NO_ERROR;
}


static svn_error_t *
rep_read_contents (void *baton, char *buf, apr_size_t *len)
{
  struct rep_read_baton *rb = baton;
  struct read_rep_args args;

  args.rb = rb;
  args.buf = buf;
  args.len = len;

  /* If we got a trail, use it; else make one. */
  if (rb->trail)
    SVN_ERR (txn_body_read_rep (&args, rb->trail));
  else
    {
      /* Hey, guess what?  trails don't clear their own subpools.  In
         the case of reading from the db, any returned data should
         live in our pre-allocated buffer, so the whole operation can
         happen within a single malloc/free cycle.  This prevents us
         from creating millions of unnecessary trail subpools when
         reading a big file. */
      apr_pool_t *subpool = svn_pool_create (rb->pool);
      SVN_ERR (svn_fs_base__retry_txn (rb->fs,
                                       txn_body_read_rep,
                                       &args,
                                       subpool));
      svn_pool_destroy (subpool);
    }
  return SVN_NO_ERROR;
}


/** Writing. **/


struct rep_write_baton
{
  /* The FS in which we're writing. */
  svn_fs_t *fs;

  /* The representation skel whose contents we want to write. */
  const char *rep_key;

  /* The transaction id under which this write action will take
     place. */
  const char *txn_id;

  /* If present, do the write as part of this trail, and use trail's
     pool.  Otherwise, see `pool' below.  */
  trail_t *trail;

  /* MD5 checksum.  Initialized when the baton is created, updated as
     we write data, and finalized and stored when the stream is
     closed. */
  struct apr_md5_ctx_t md5_context;
  unsigned char md5_digest[APR_MD5_DIGESTSIZE];
  svn_boolean_t finalized;

  /* Used for temporary allocations, iff `trail' (above) is null.  */
  apr_pool_t *pool;

};


static struct rep_write_baton *
rep_write_get_baton (svn_fs_t *fs,
                     const char *rep_key,
                     const char *txn_id,
                     trail_t *trail,
                     apr_pool_t *pool)
{
  struct rep_write_baton *b;

  b = apr_pcalloc (pool, sizeof (*b));
  apr_md5_init (&(b->md5_context));
  b->fs = fs;
  b->trail = trail;
  b->pool = pool;
  b->rep_key = rep_key;
  b->txn_id = txn_id;
  return b;
}



/* Write LEN bytes from BUF into the end of the string represented via
   REP_KEY in FS, as part of TRAIL.  If the representation is not
   mutable, return the error SVN_FS_REP_NOT_MUTABLE. */
static svn_error_t *
rep_write (svn_fs_t *fs,
           const char *rep_key,
           const char *buf,
           apr_size_t len,
           const char *txn_id,
           trail_t *trail)
{
  representation_t *rep;

  SVN_ERR (svn_fs_bdb__read_rep (&rep, fs, rep_key, trail));

  if (! rep_is_mutable (rep, txn_id))
    return svn_error_createf
      (SVN_ERR_FS_REP_NOT_MUTABLE, NULL,
       "Rep '%s' is not mutable", rep_key);

  if (rep->kind == rep_kind_fulltext)
    {
      SVN_ERR (svn_fs_bdb__string_append
               (fs, &(rep->contents.fulltext.string_key), len, buf, trail));
    }
  else if (rep->kind == rep_kind_delta)
    {
      /* There should never be a case when we have a mutable
         non-fulltext rep.  The only code that creates mutable reps is
         in this file, and it creates them fulltext. */
      return svn_error_createf
        (SVN_ERR_FS_CORRUPT, NULL,
         "Rep '%s' both mutable and non-fulltext", rep_key);
    }
  else /* unknown kind */
    abort ();

  return SVN_NO_ERROR;
}


struct write_rep_args
{
  struct rep_write_baton *wb;   /* Destination.       */
  const char *buf;              /* Data.              */
  apr_size_t len;               /* How much to write. */
};


/* BATON is of type `write_rep_args':
   Append onto BATON->wb->rep_key's contents BATON->len bytes of
   data from BATON->wb->buf, in BATON->rb->fs, as part of TRAIL.

   If the representation is not mutable, return the error
   SVN_FS_REP_NOT_MUTABLE.  */
static svn_error_t *
txn_body_write_rep (void *baton, trail_t *trail)
{
  struct write_rep_args *args = baton;

  SVN_ERR (rep_write (args->wb->fs,
                      args->wb->rep_key,
                      args->buf,
                      args->len,
                      args->wb->txn_id,
                      trail));

  apr_md5_update (&(args->wb->md5_context), args->buf, args->len);

  return SVN_NO_ERROR;
}


static svn_error_t *
rep_write_contents (void *baton,
                    const char *buf,
                    apr_size_t *len)
{
  struct rep_write_baton *wb = baton;
  struct write_rep_args args;

  /* We toss LEN's indirectness because if not all the bytes are
     written, it's an error, so we wouldn't be reporting anything back
     through *LEN anyway. */
  args.wb = wb;
  args.buf = buf;
  args.len = *len;

  /* If we got a trail, use it; else make one. */
  if (wb->trail)
    SVN_ERR (txn_body_write_rep (&args, wb->trail));
  else
    {
      /* Hey, guess what?  trails don't clear their own subpools.  In
         the case of simply writing the rep to the db, we're *certain*
         that there's no data coming back to us that needs to be
         preserved... so the whole operation can happen within a
         single malloc/free cycle.  This prevents us from creating
         millions of unnecessary trail subpools when writing a big
         file. */
      apr_pool_t *subpool = svn_pool_create (wb->pool);
      SVN_ERR (svn_fs_base__retry_txn (wb->fs,
                                       txn_body_write_rep,
                                       &args,
                                       subpool));
      svn_pool_destroy (subpool);
    }

  return SVN_NO_ERROR;
}


/* Helper for rep_write_close_contents(); see that doc string for
   more.  BATON is of type `struct rep_write_baton'. */
static svn_error_t *
txn_body_write_close_rep (void *baton, trail_t *trail)
{
  struct rep_write_baton *wb = baton;
  representation_t *rep;

  SVN_ERR (svn_fs_bdb__read_rep (&rep, wb->fs, wb->rep_key, trail));
  memcpy (rep->checksum, wb->md5_digest, APR_MD5_DIGESTSIZE);
  SVN_ERR (svn_fs_bdb__write_rep (wb->fs, wb->rep_key, rep, trail));

  return SVN_NO_ERROR;
}


/* BATON is of type `struct rep_write_baton'.
 *
 * Finalize BATON->md5_context and store the resulting digest under
 * BATON->rep_key.
 */
static svn_error_t *
rep_write_close_contents (void *baton)
{
  struct rep_write_baton *wb = baton;

  /* ### Thought: if we fixed apr-util MD5 contexts to allow repeated
     digestification, then we wouldn't need a stream close function at
     all -- instead, we could update the stored checksum each time a
     write occurred, which would have the added advantage of making
     interleaving reads and writes work.  Currently, they'd fail with
     a checksum mismatch, it just happens that our code never tries to
     do that anyway. */

  if (! wb->finalized)
    {
      apr_md5_final (wb->md5_digest, &wb->md5_context);
      wb->finalized = TRUE;
    }

  /* If we got a trail, use it; else make one. */
  if (wb->trail)
    {
      SVN_ERR (txn_body_write_close_rep (wb, wb->trail));
    }
  else
    {
      SVN_ERR (svn_fs_base__retry_txn (wb->fs,
                                       txn_body_write_close_rep,
                                       wb,
                                       wb->pool));
    }

  return SVN_NO_ERROR;
}


/** Public read and write stream constructors. **/

svn_error_t *
svn_fs_base__rep_contents_read_stream (svn_stream_t **rs_p,
                                       svn_fs_t *fs,
                                       const char *rep_key,
                                       svn_boolean_t use_trail_for_reads,
                                       trail_t *trail,
                                       apr_pool_t *pool)
{
  struct rep_read_baton *rb;

  SVN_ERR (rep_read_get_baton (&rb, fs, rep_key, use_trail_for_reads,
                               trail, pool));
  *rs_p = svn_stream_create (rb, pool);
  svn_stream_set_read (*rs_p, rep_read_contents);

  return SVN_NO_ERROR;
}


/* Clear the contents of REP_KEY, so that it represents the empty

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -