/* -*- 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 <sqlite3.h>
#include <assert.h>
#include <string.h> /* strlen() */
#include <stddef.h> /* NULL on linux */
#include <zlib.h>
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 = (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 if(!len) return 0;
else{
fsl_size_t sz;
int rc;
if(len<0){
len = (fsl_int_t)fsl_strlen((char const *)data);
if(!len) return 0;
}
sz = b->used + 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(total<rlen){
/* Overflow! */
rc = FSL_RC_RANGE;
break;
}
if( dest->capacity < (total+1) ){
rc = fsl_buffer_reserve( dest,
total + ((rlen<BufSize) ? 1 : BufSize)
/* Probably unnecessarily clever :/ */
);
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_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);
}