/*
** 2014-06-13
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
******************************************************************************
**
** This SQLite extension implements SQL compression functions
** compress() and uncompress() using ZLIB.
*/
#include "sqlite3ext.h"
SQLITE_EXTENSION_INIT1
#include <zlib.h>

/*
** Implementation of the "compress(X)" SQL function.  The input X is
** compressed using zLib and the output is returned.
**
** The output is a BLOB that begins with an integer that forming the
** input size in bytes (the size of X before compression).  The variable-
** length integer is implemented as 1 to 5 bytes.  If the first byte is 0,
** 4 bytes are used, the most significant bits first. Otherwise there are
** seven bits per integer stored in the lower seven bits of each byte.
** More significant bits occur first.  The most significant bit (0x80)
** is a flag to indicate the end of the integer.
**
** This function, SQLAR, and ZIP all use the same "deflate" compression
** algorithm, but each is subtly different:
**
**   *  ZIP uses raw deflate.
**
**   *  SQLAR uses the "zlib format" which is raw deflate with a two-byte
**      algorithm-identification header and a four-byte checksum at the end.
**
**   *  This utility uses the "zlib format" like SQLAR, but adds the variable-
**      length integer uncompressed size value at the beginning.
**
** This function might be extended in the future to support compression
** formats other than deflate, by providing a different algorithm-id
** mark following the variable-length integer size parameter.
*/
static void compressFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const unsigned char *pIn;
  unsigned char *pOut;
  size_t nIn;
  unsigned long int nOut;
  int j, rc;

  pIn = sqlite3_value_blob(argv[0]);
  nIn = sqlite3_value_bytes(argv[0]);
  nOut = 13 + nIn + (nIn+999)/1000;
  pOut = sqlite3_malloc( nOut+5 );
  if( (nIn<=0xffffff) || (!(nIn&0x80)&&!(nIn&0x8000)&&!(nIn&0x800000))) {
    pOut[0] = nIn>>24 & 0xff;
    pOut[1] = nIn>>16 & 0xff;
    pOut[2] = nIn>>8 & 0xff;
    pOut[3] = nIn & 0xff;
    j = 4;
  }else{
    int i;
    unsigned char x[8];
    for(i=4; i>=0; i--){
      x[i] = (nIn >> (7*(4-i)))&0x7f;
    }
    for(i=0; i<4 && x[i]==0; i++){}
    for(j=0; i<=4; i++, j++) pOut[j] = x[i];
    pOut[j-1] |= 0x80;
  }
  rc = compress(&pOut[j], &nOut, pIn, nIn);
  if( rc==Z_OK ){
    sqlite3_result_blob(context, pOut, nOut+j, sqlite3_free);
  }else{
    sqlite3_free(pOut);
    sqlite3_result_error(context, "input cannot be zlib compressed", -1);
  }
}

/*
** Implementation of the "uncompress(X)" SQL function.  The argument X
** is a blob which was obtained from compress(Y).  The output will be
** the value Y.
*/
static void uncompressFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const unsigned char *pIn;
  unsigned char *pOut;
  unsigned int nIn;
  unsigned long nOut;
  int rc;

  pIn = sqlite3_value_blob(argv[0]);
  nIn = sqlite3_value_bytes(argv[0]);
  nOut = (pIn[0]<<24) + (pIn[1]<<16) + (pIn[2]<<8) + pIn[3];
  if( pIn[0] ){
    unsigned long nOut2 = 0;
    int i;

    /*
    ** If the high-byte of the blob length > 0, there are actually
    ** two possibilities:
    **  1) The blob is > 16MByte, possible but unlikely as most blobs
    **     are not that big.
    **  2) The content was compressed with SQLite's ext/misc/compress.c
    **
    ** Just try both possibilities (smallest first), if the decompression
    ** fails (either by Z_BUF_ERROR or Z_DATA_ERROR) we will find out quick
    ** enough which one was correct.
    */
    for(i=0; i<nIn && i<5; i++){
      nOut2 = (nOut2<<7) | (pIn[i]&0x7f);
      if( (pIn[i]&0x80)!=0 ){ i++; break; }
    }
    if( (nOut < nOut2) || (i==5 && (pIn[4]^0x80)&0xf0) ){
      pOut = sqlite3_malloc( nOut+1 );
      rc = uncompress(pOut, &nOut, &pIn[4], nIn-4);
      if( rc==Z_OK ){
        sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
        return;
      }
      sqlite3_free(pOut);
    }
    pOut = sqlite3_malloc( nOut2+1 );
    rc = uncompress(pOut, &nOut2, &pIn[i], nIn-i);
    if( rc==Z_OK ){
      sqlite3_result_blob(context, pOut, nOut2, sqlite3_free);
      return;
    }
    if( nOut < nOut2 ){
      goto error;
    }
    sqlite3_free(pOut);
  }
  pOut = sqlite3_malloc( nOut+1 );
  rc = uncompress(pOut, &nOut, &pIn[4], nIn-4);
  if( rc==Z_OK ){
    sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
  }else{
  error:
    sqlite3_free(pOut);
    sqlite3_result_error(context, "input is not zlib compressed", -1);
  }
}


#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_compress_init(
  sqlite3 *db, 
  char **pzErrMsg, 
  const sqlite3_api_routines *pApi
){
  int rc = SQLITE_OK;
  SQLITE_EXTENSION_INIT2(pApi);
  (void)pzErrMsg;  /* Unused parameter */
  rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
                               compressFunc, 0, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
                                 uncompressFunc, 0, 0);
  }
  return rc;
}
#if !defined(_WIN32) && !defined(SQLITE_TEST)
int sqlite3_extension_init(
  sqlite3 *db, 
  char **pzErrMsg,
  const sqlite3_api_routines *pApi
){
  int rc = SQLITE_OK;
  SQLITE_EXTENSION_INIT2(pApi);
  (void)pzErrMsg;  /* Unused parameter */
  rc = sqlite3_create_function(db, "compress", 1, SQLITE_UTF8, 0,
                               compressFunc, 0, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "uncompress", 1, SQLITE_UTF8, 0,
                                 uncompressFunc, 0, 0);
  }
  return rc;
}
#endif
