/**
Copyright (c) 2016-2019, Powturbo
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    - homepage : https://sites.google.com/site/powturbo/
    - github   : https://github.com/powturbo
    - twitter  : https://twitter.com/powturbo
    - email    : powturbo [_AT_] gmail [_DOT_] com
**/
#pragma once
#ifndef _TURBOB64_H_
#define _TURBOB64_H_
#ifdef __cplusplus
extern "C" {
#endif

#define TB64_VERSION 100
//---------------------- base64 API functions ----------------------------------
// return the base64 buffer length after encoding
size_t tb64enclen(size_t inlen);

// return the original (after decoding) length for a given base64 encoded buffer
size_t tb64declen(const unsigned char *in, size_t inlen);

// Encode binary input 'in' buffer into base64 string 'out' 
// with automatic cpu detection for avx2/sse4.1/scalar 
// in          : Input buffer to encode
// inlen       : Length in bytes of input buffer
// out         : Output buffer
// return value: Length of output buffer
// Remark      : byte 'zero' is not written to end of output stream
//               Caller must add 0 (out[outlen] = 0) for a null terminated string
size_t tb64enc(const unsigned char *in, size_t inlen, unsigned char *out);

// Decode base64 input 'in' buffer into binary buffer 'out' 
// in          : input buffer to decode
// inlen       : length in bytes of input buffer 
// out         : output buffer
// return value: >0 output buffer length
//                0 Error (invalid base64 input or input length = 0)
size_t tb64dec(const unsigned char *in, size_t inlen, unsigned char *out);

//------ Direct call to tb64enc + tb64dec ---------------------------------------
// Direct call to tb64enc + tb64dec saving a function call + a check instruction
// call tb64ini, then call _tb64e(in, inlen, out) or _tb64d(in, inlen, out)
typedef size_t (*TB64FUNC)(const unsigned char *in, size_t n, unsigned char *out);

extern TB64FUNC _tb64e;
extern TB64FUNC _tb64d;

//---------------------- base64 Internal functions ------------------------------
// Base64 output length after encoding 
#define TB64ENCLEN(_n_) ((_n_ + 2)/3 * 4)

// Memory efficient (small lookup tables) scalar but (slower) version
size_t tb64senc(   const unsigned char *in, size_t inlen, unsigned char *out);
size_t tb64sdec(   const unsigned char *in, size_t inlen, unsigned char *out);

// Fast scalar
size_t tb64xenc(   const unsigned char *in, size_t inlen, unsigned char *out);
size_t tb64xdec(   const unsigned char *in, size_t inlen, unsigned char *out);

// ssse3  
size_t tb64sseenc( const unsigned char *in, size_t inlen, unsigned char *out);
size_t tb64ssedec( const unsigned char *in, size_t inlen, unsigned char *out);

// avx 
size_t tb64avxenc( const unsigned char *in, size_t inlen, unsigned char *out);
size_t tb64avxdec( const unsigned char *in, size_t inlen, unsigned char *out);

// avx2
size_t tb64avx2enc(const unsigned char *in, size_t inlen, unsigned char *out);
size_t tb64avx2dec(const unsigned char *in, size_t inlen, unsigned char *out);

// avx512
size_t tb64avx512enc(const unsigned char *in, size_t inlen, unsigned char *out);
size_t tb64avx512dec(const unsigned char *in, size_t inlen, unsigned char *out);

// detect cpu && set the default run time functions for tb64enc/tb64dec
// isshort = 0 : default
// isshort > 0 : set optimized short strings version (actually only avx2)
void tb64ini(unsigned id, unsigned isshort);  

//------- optimized functions for short strings only --------------------------
// - decoding without checking  
// - can read beyond the input buffer end, 
//   therefore input buffer size must be 32 bytes larger than input length
size_t _tb64avx2enc(const unsigned char *in, size_t inlen, unsigned char *out);
size_t _tb64avx2dec(const unsigned char *in, size_t inlen, unsigned char *out);

//------- CPU instruction set ----------------------
// cpuisa  = 0: return current simd set, 
// cpuisa != 0: set simd set 0:scalar, 0x33:sse2, 0x60:avx2
unsigned cpuini(unsigned cpuisa); 

// convert simd set to string "sse3", "ssse3", "sse4.1", "avx", "avx2", "neon",... 
// Ex.: printf("current cpu set=%s\n", cpustr(cpuini(0)) ); 
char *cpustr(unsigned cpuisa); 

#ifdef __cplusplus
}
#endif
#endif
