/* -*- 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/fossil.h" #include #include #include /* strlen() */ #include /* NULL on linux */ #include 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; } } 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 + len + 1/*NUL*/; rc = fsl_buffer_reserve( b, sz ); if(rc) return rc; else{ assert(b->capacity >= sz); memcpy(b->mem + b->used, data, (size_t)len); b->used += len; b->mem[b->used] = 0; return 0; } } } /* ** 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 long fsl_appendf_f_buffer( void * arg, char const * data, long n ){ BufferAppender * ba = (BufferAppender*)arg; fsl_buffer * sb = ba->b; if( !sb || (n<0) ) return -1; else if( ! n ) return 0; else{ long rc; 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; else return ('x'==mem[4]) && (0234==mem[5]); } char fsl_buffer_is_compressed(fsl_buffer const *buf){ return buf ? fsl_data_is_compressed( buf->mem, buf->used ) : 0; } 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 *pIn1, fsl_buffer *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_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 = (('-'==*filename)&&!*(filename+1)) ? stdin : fopen(filename,"rb"); if(!src) return FSL_RC_IO; rc = fsl_buffer_fill_from( dest, fsl_input_FILE, src ); if(stdin!=src) 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); }