Login
Artifact [aed0ae8467]
Login

Artifact aed0ae846717b2fdb8c64db81bfe2b344a1c6a04:


/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
/*
** Copyright (c) 2013 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** 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.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
*/
#include "fossil-scm/fossil.h"
#include <sqlite3.h>
#include <assert.h>
#include <string.h> /* strlen() */
#include <stddef.h> /* NULL on linux */
#include <errno.h>

#include <zlib.h>


#define MARKER(pfexp)                                               \
  do{ printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__);   \
    printf pfexp;                                                   \
  } while(0)


int fsl_buffer_reset( fsl_buffer * b ){
  if(!b) return FSL_RC_MISUSE;
  else{
    if(b->capacity){
      assert(b->mem);
      b->mem[0] = 0;
    }
    b->used = 0;
    return 0;
  }
}

void fsl_buffer_clear( fsl_buffer * buf ){
  if(buf){
    if(buf->mem) fsl_free(buf->mem);
    *buf = fsl_buffer_empty;
  }
}

int fsl_buffer_reserve( fsl_buffer * buf, fsl_size_t n ){
  if( ! buf ) return FSL_RC_MISUSE;
  else if( 0 == n ){
    fsl_free(buf->mem);
    *buf = fsl_buffer_empty;
    return 0;
  }
  else if( buf->capacity >= n ){
    return 0;
  }
  else{
    unsigned char * x = (unsigned char *)fsl_realloc( buf->mem, n );
    if( ! x ) return FSL_RC_OOM;
    memset( x + buf->used, 0, n - buf->used );
    buf->mem = x;
    buf->capacity = n;
    return 0;
  }
}

int fsl_buffer_resize( fsl_buffer * buf, fsl_size_t n ){
  if( !buf ) return FSL_RC_MISUSE;
  else if(n && (buf->capacity == n+1)){
    buf->used = n;
    buf->mem[n] = 0;
    return 0;
  }
  else {
    unsigned char * x = (unsigned char *)fsl_realloc( buf->mem,
                                                      n+1/*NUL*/ );
    if( ! x ) return FSL_RC_OOM;
    if(n > buf->capacity){
      /* zero-fill new parts */
      memset( x + buf->capacity, 0, n - buf->capacity +1/*NUL*/ );
    }
    buf->capacity = n + 1 /*NUL*/;
    buf->used = n;
    buf->mem = x;
    buf->mem[buf->used] = 0;
    return 0;
  }
}

int fsl_buffer_compare(fsl_buffer const * lhs, fsl_buffer const * rhs){
  fsl_size_t const szL = lhs->used;
  fsl_size_t const szR = rhs->used;
  fsl_size_t const sz = (szL<szR) ? szL : szR;
  int rc = memcmp(lhs->mem, rhs->mem, sz);
  if(0 == rc){
    rc = (szL==szR)
      ? 0
      : ((szL<szR) ? -1 : 1);
  }
  return rc;
}

/*
** Compare two blobs in constant time and return zero if they are equal.
** Constant time comparison only applies for blobs of the same length.
** If lengths are different, immediately returns 1.
*/
int fsl_buffer_compare_O1(fsl_buffer const * lhs, fsl_buffer const * rhs){
  fsl_size_t const szL = lhs->used;
  fsl_size_t const szR = rhs->used;
  fsl_size_t i;
  unsigned char const *buf1;
  unsigned char const *buf2;
  unsigned char rc = 0;
  if( szL!=szR || szL==0 ) return 1;
  buf1 = lhs->mem;
  buf2 = rhs->mem;
  for( i=0; i<szL; i++ ){
    rc = rc | (buf1[i] ^ buf2[i]);
  }
  return rc;
}


int fsl_buffer_append( fsl_buffer * b,
                       void const * data,
                       fsl_int_t len ){
  if(!b || !data) return FSL_RC_MISUSE;
  else{
    fsl_size_t sz = b->used;
    int rc = 0;
    assert(b->capacity ? !!b->mem : !b->mem);
    assert(b->used <= b->capacity);
    if(len<0){
      len = (fsl_int_t)fsl_strlen((char const *)data);
    }
    sz += len + 1/*NUL*/;
    rc = fsl_buffer_reserve( b, sz );
    if(!rc){
      assert(b->capacity >= sz);
      if(len>0) memcpy(b->mem + b->used, data, (size_t)len);
      b->used += len;
      b->mem[b->used] = 0;
    }
    return rc;
  }
}

/*
** Internal helper for implementing fsl_buffer_appendf()
*/
typedef struct BufferAppender {
  fsl_buffer * b;
  /*
  ** Result code of the appending process.
  */
  int rc;
} BufferAppender;

/*
** fsl_appendf_f() impl which requires arg to be a (fsl_buffer*).
** It appends the data to arg.
*/
static fsl_int_t fsl_appendf_f_buffer( void * arg,
                                       char const * data, fsl_int_t n ){
  BufferAppender * ba = (BufferAppender*)arg;
  fsl_buffer * sb = ba->b;
  if( !sb || (n<0) ) return -1;
  else if( ! n ) return 0;
  else{
    fsl_int_t rc;
    fsl_size_t npos = sb->used + n;
    if( npos >= sb->capacity ){
      const size_t asz = npos ? ((4 * npos / 3) + 1) : 16;
      if( asz < npos ) {
        ba->rc = FSL_RC_RANGE;
        return -1; /* overflow */
      }
      else{
        rc = fsl_buffer_reserve( sb, asz );
        if(rc) {
          ba->rc = FSL_RC_OOM;
          return -1;
        }
      }
    }
    rc = 0;
    for( ; rc < n; ++rc, ++sb->used ){
      sb->mem[sb->used] = data[rc];
    }
    sb->mem[sb->used] = 0;
    return rc;
  }
}

int fsl_buffer_appendfv( fsl_buffer * b,
                         char const * fmt, va_list args){
  if(!b || !fmt) return FSL_RC_MISUSE;
  else{
    BufferAppender ba;
    ba.b = b;
    ba.rc = 0;
    fsl_appendfv( fsl_appendf_f_buffer, &ba, fmt, args );
    return ba.rc;
  }
}

char const * fsl_buffer_cstr(fsl_buffer const *b){
  return b ? (char const *)b->mem : NULL;
}
char const * fsl_buffer_cstr2(fsl_buffer const *b, fsl_size_t * len){
  char const * rc = NULL;
  if(b){
    rc = (char const *)b->mem;
    if(len) *len = b->used;
  }
  return rc;
}

char * fsl_buffer_str(fsl_buffer const *b){
  return b ? (char *)b->mem : NULL;
}


fsl_size_t fsl_buffer_size(fsl_buffer const * b){
  return b ? b->used : 0U;
}

fsl_size_t fsl_buffer_capacity(fsl_buffer const * b){
  return b ? b->capacity : 0;
}


int fsl_buffer_appendf( fsl_buffer * b,
                        char const * fmt, ... ){
  if(!b || !fmt) return FSL_RC_MISUSE;
  else{
    int rc;
    va_list args;
    va_start(args,fmt);
    rc = fsl_buffer_appendfv( b, fmt, args );
    va_end(args);
    return rc;
  }
}

char fsl_data_is_compressed(unsigned char const * mem, fsl_size_t len){
  if(!mem || (len<6)) return 0;
#if 1
  else return ('x'==mem[4])
    && (0234==mem[5]);
#else
  else{
    /**
       Adapted from:
       
       http://blog.2of1.org/2011/03/03/decompressing-zlib-images/

       Remember that fossil-compressed data has a 4-byte big-endian
       header holding the uncompressed size of the data, so we skip
       those first 4 bytes.

       See also:

       http://tools.ietf.org/html/rfc6713

       search for "magic number".
    */
    fsl_int16_t const head = (((fsl_int16_t)mem[4]) << 8) | mem[5];
    /* MARKER(("isCompressed header=%04x\n", head)); */
    switch(head){
      case 0x083c: case 0x087a: case 0x08b8: case 0x08f6:
      case 0x1838: case 0x1876: case 0x18b4: case 0x1872:
      case 0x2834: case 0x2872: case 0x28b0: case 0x28ee:
      case 0x3830: case 0x386e: case 0x38ac: case 0x38ea:
      case 0x482c: case 0x486a: case 0x48a8: case 0x48e6:
      case 0x5828: case 0x5866: case 0x58a4: case 0x58e2:
      case 0x6824: case 0x6862: case 0x68bf: case 0x68fd:
      case 0x7801: case 0x785e: case 0x789c: case 0x78da:
        return 1;
      default:
        return 0;
    }
  }
#endif
}

char fsl_buffer_is_compressed(fsl_buffer const *buf){
  return buf
    ? fsl_data_is_compressed( buf->mem, buf->used )
    : 0;
}

fsl_int_t fsl_data_uncompressed_size(unsigned char const *mem,
                                     fsl_size_t len){
  return fsl_data_is_compressed(mem,len)
    ? ((mem[0]<<24) + (mem[1]<<16) + (mem[2]<<8) + mem[3])
    : -1;
}

fsl_int_t fsl_buffer_uncompressed_size(fsl_buffer const * b){
  return b
    ? fsl_data_uncompressed_size(b->mem, b->used)
    : -1;
}

int fsl_buffer_compress(fsl_buffer const *pIn, fsl_buffer *pOut){
  unsigned int nIn = pIn->used;
  unsigned int nOut = 13 + nIn + (nIn+999)/1000;
  fsl_buffer temp = fsl_buffer_empty;
  int rc = fsl_buffer_resize(&temp, nOut+4);
  if(rc) return rc;
  else{
    unsigned long int nOut2;
    unsigned char *outBuf;
    outBuf = temp.mem;
    outBuf[0] = nIn>>24 & 0xff;
    outBuf[1] = nIn>>16 & 0xff;
    outBuf[2] = nIn>>8 & 0xff;
    outBuf[3] = nIn & 0xff;
    nOut2 = (long int)nOut;
    rc = compress(&outBuf[4], &nOut2,
                  pIn->mem, pIn->used);
    if(rc){
      fsl_buffer_reserve(&temp, 0);
      return FSL_RC_ERROR;
    }
    fsl_buffer_reserve(pOut, 0);
    *pOut = temp;
    if(!rc){
      rc = fsl_buffer_resize(pOut, nOut2+4);
      if(!rc){
        pOut->used = nOut2+4;
      }
    }
    return rc;
  }
}

int fsl_buffer_compress2(fsl_buffer const *pIn1,
                         fsl_buffer const *pIn2, fsl_buffer *pOut){
  unsigned int nIn = pIn1->used + pIn2->used;
  unsigned int nOut = 13 + nIn + (nIn+999)/1000;
  fsl_buffer temp = fsl_buffer_empty;
  int rc;
  rc = fsl_buffer_resize(&temp, nOut+4);
  if(rc) return rc;
  else{
    unsigned char *outBuf;
    z_stream stream;
    outBuf = temp.mem;
    outBuf[0] = nIn>>24 & 0xff;
    outBuf[1] = nIn>>16 & 0xff;
    outBuf[2] = nIn>>8 & 0xff;
    outBuf[3] = nIn & 0xff;
    stream.zalloc = (alloc_func)0;
    stream.zfree = (free_func)0;
    stream.opaque = 0;
    stream.avail_out = nOut;
    stream.next_out = &outBuf[4];
    deflateInit(&stream, 9);
    stream.avail_in = pIn1->used;
    stream.next_in = pIn1->mem;
    deflate(&stream, 0);
    stream.avail_in = pIn2->used;
    stream.next_in = pIn2->mem;
    deflate(&stream, 0);
    deflate(&stream, Z_FINISH);
    rc = fsl_buffer_resize(&temp, stream.total_out + 4);
    deflateEnd(&stream);
    if(!rc){
      temp.used = stream.total_out + 4;
      if( pOut==pIn1 ) fsl_buffer_reserve(pOut, 0);
      else if( pOut==pIn2 ) fsl_buffer_reserve(pOut, 0);
      assert(!pOut->mem);
      *pOut = temp;
    }else{
      fsl_buffer_reserve(&temp, 0);
    }
    return rc;
  }
}

int fsl_buffer_uncompress(fsl_buffer const *pIn, fsl_buffer *pOut){
  unsigned int nOut;
  unsigned char *inBuf;
  unsigned int nIn = pIn->used;
  fsl_buffer temp = fsl_buffer_empty;
  int rc;
  unsigned long int nOut2;
  if( nIn<=4 ){
    return FSL_RC_RANGE;
  }
  inBuf = pIn->mem;
  nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3];
  rc = fsl_buffer_reserve(&temp, nOut+1);
  if(rc) return rc;
  nOut2 = (long int)nOut;
  rc = uncompress(temp.mem, &nOut2,
                  &inBuf[4], nIn - 4)
    /* valgrind says there's an uninitialized memory access
       somewhere under uncompress(), _presumably_ for one of
       these arguments, but i can't find it. fsl_buffer_reserve()
       always memsets() new bytes to 0.

       Turns out it's a known problem:

       http://www.zlib.net/zlib_faq.html#faq36
    */;
  if( rc!=Z_OK ){
    fsl_buffer_reserve(&temp, 0);
    return FSL_RC_ERROR;
  }
  rc = fsl_buffer_resize(&temp, nOut2);
  if(!rc){
    temp.used = (fsl_size_t)nOut2;
    if( pOut==pIn ){
      fsl_buffer_reserve(pOut, 0);
    }
    assert(!pOut->mem);
    *pOut = temp;
  }else{
    fsl_buffer_reserve(&temp, 0);
  }
  return rc;
}


int fsl_buffer_fill_from( fsl_buffer * dest, fsl_input_f src, void * state )
{
  int rc;
  enum { BufSize = 512 * 4 };
  char rbuf[BufSize];
  fsl_size_t total = 0;
  fsl_size_t rlen = 0;
  if( !dest || ! src ) return FSL_RC_MISUSE;
  dest->used = 0;
  while(1){
    rlen = BufSize;
    rc = src( state, rbuf, &rlen );
    if( rc ) break;
    total += rlen;
    if(total<rlen){
      /* Overflow! */
      rc = FSL_RC_RANGE;
      break;
    }
    if( dest->capacity < (total+1) ){
      rc = fsl_buffer_reserve( dest,
                               total + ((rlen<BufSize) ? 1 : BufSize)
                               );
      if( 0 != rc ) break;
    }
    memcpy( dest->mem + dest->used, rbuf, rlen );
    dest->used += rlen;
    if( rlen < BufSize ) break;
  }
  if( !rc && dest->used ){
    assert( dest->used < dest->capacity );
    dest->mem[dest->used] = 0;
  }
  return rc;
}

int fsl_buffer_fill_from_FILE( fsl_buffer * dest, FILE * src ){
  return (!dest || !src)
          ? FSL_RC_MISUSE
          : fsl_buffer_fill_from( dest, fsl_input_f_FILE, src );
}          


int fsl_buffer_fill_from_filename( fsl_buffer * dest, char const * filename ){
  if(!dest || !filename || !*filename) return FSL_RC_MISUSE;
  else{
    int rc;
    FILE * src;
    fsl_fstat st = fsl_fstat_empty;
    rc = fsl_stat( filename, &st, 1 );
    if(rc && st.size>0){ /* Might not be a real file, e.g. "-" */
      rc = fsl_buffer_reserve(dest, st.size+1);
      if(rc) return rc;
    }
    src = fsl_fopen(filename,"rb");
    if(!src) rc = fsl_errno_to_rc(errno, FSL_RC_IO);
    else {
      rc = fsl_buffer_fill_from( dest, fsl_input_f_FILE, src );
      fsl_fclose(src);
    }
    return rc;
  }
}

void fsl_buffer_swap( fsl_buffer * left, fsl_buffer * right ){
  fsl_buffer const tmp = *left;
  *left = *right;
  *right = tmp;
}

void fsl_buffer_swap_free( fsl_buffer * left, fsl_buffer * right, char clearWhich ){
  fsl_buffer_swap(left, right);
  if(0 != clearWhich) fsl_buffer_reserve((clearWhich<0) ? left : right, 0);
}

int fsl_buffer_copy( fsl_buffer const * src, fsl_buffer * dest ){
  dest->used = 0;
  return src->used
    ? fsl_buffer_append( dest, src->mem, src->used )
    : 0;
}

int fsl_buffer_delta_apply2( fsl_buffer const * orig,
                             fsl_buffer const * pDelta,
                             fsl_buffer * pTarget,
                             fsl_error * pErr){
  int rc;
  fsl_size_t n = 0;
  fsl_buffer out = fsl_buffer_empty;
  rc = fsl_delta_applied_size( pDelta->mem, pDelta->used, &n);
  if(rc){
    if(pErr){
      fsl_error_set(pErr, rc, "fsl_delta_applied_size() failed.");
    }
    return rc;
  }
  rc = fsl_buffer_resize( &out, n );
  if(rc) return rc;
  rc = fsl_delta_apply2( orig->mem, orig->used,
                        pDelta->mem, pDelta->used,
                        out.mem, pErr);
  if(rc){
    fsl_buffer_clear(&out);
  }else{
    fsl_buffer_clear(pTarget);
    *pTarget = out;
  }
  return rc;
}

int fsl_buffer_delta_apply( fsl_buffer const * orig,
                            fsl_buffer const * pDelta,
                            fsl_buffer * pTarget){
  return fsl_buffer_delta_apply2(orig, pDelta, pTarget, NULL);
}

void fsl_buffer_defossilize( fsl_buffer * b ){
  if(b){
    fsl_bytes_defossilize( b->mem, &b->used );
  }
}

int fsl_buffer_to_filename( fsl_buffer * b, char const * fname ){
  FILE * f;
  int rc = 0;
  if(!b || !fname) return FSL_RC_MISUSE;
  f = fsl_fopen(fname, "wb");
  if(!f) rc = fsl_errno_to_rc(errno, FSL_RC_IO);
  else if(b->used) {
    size_t const frc = fwrite(b->mem, b->used, 1, f);
    rc = (1==frc) ? 0 : FSL_RC_IO;
  }
  fsl_fclose(f);
  return rc;
}

int fsl_buffer_delta_create( fsl_buffer const * src,
                             fsl_buffer const * newVers,
                             fsl_buffer * delta){
  if(!src || !newVers || !delta) return FSL_RC_MISUSE;
  else if((src == newVers)
          || (src==delta)
          || (newVers==delta)) return FSL_RC_MISUSE;
  else{
    int rc = fsl_buffer_reserve( delta, newVers->used + 60 );
    if(!rc){
      delta->used = 0;
      rc = fsl_delta_create( src->mem, src->used,
                             newVers->mem, newVers->used,
                             delta->mem, &delta->used );
      if(!rc){
        rc = fsl_buffer_resize( delta, delta->used );
      }
    }
    return rc;
  }
}


int fsl_output_f_buffer( void * state,
                         void const * src, fsl_size_t n ){
  return !state
    ? FSL_RC_MISUSE
    : fsl_buffer_append((fsl_buffer*)state, src, n);
}

int fsl_finalizer_f_buffer( void * state, void * mem ){
  fsl_buffer * b = (fsl_buffer*)mem;
  fsl_buffer_reserve(b, 0);
  *b = fsl_buffer_empty;
  return 0;
}

#undef MARKER