/* -*- 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 #include #include /* strlen() */ #include /* NULL on linux */ #include #include #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 = (szLmem, rhs->mem, sz); if(0 == rc){ rc = (szL==szR) ? 0 : ((szLused; 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; iused; 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(totalcapacity < (total+1) ){ rc = fsl_buffer_reserve( dest, total + ((rlenmem + 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