Files
tlib/oversampling/WDL/projectcontext.cpp
2024-05-24 13:28:31 +02:00

1321 lines
29 KiB
C++

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "projectcontext.h"
#include "fileread.h"
#include "filewrite.h"
#include "heapbuf.h"
#include "wdlstring.h"
#include "wdlcstring.h"
#include "fastqueue.h"
#include "lineparse.h"
//#define WDL_MEMPROJECTCONTEXT_USE_ZLIB 1
#ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
#define WDL_MEMPROJECTCONTEXT_ZLIB_CHUNKSIZE 65536
#include "zlib/zlib.h"
#endif
#include "denormal.h"
char *projectcontext_fastDoubleToString(double value, char *bufOut, int prec_digits)
{
value = denormal_filter_double2(value);
if (value<0.0)
{
value=-value;
*bufOut++ = '-';
}
if (value < 1e-20)
{
*bufOut++ = '0';
*bufOut = 0;
return bufOut;
}
if (value > 2147483647.0)
{
if (value >= 1.0e40) sprintf(bufOut, "%e", value);
else sprintf(bufOut, "%.*f", wdl_min(prec_digits,8), value);
while (*bufOut) bufOut++;
return bufOut;
}
unsigned int value_i, frac, frac2;
int prec_digits2 = 0;
if (prec_digits>0)
{
static const unsigned int scales[9] =
{
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000
};
value_i = (unsigned int)value;
const int value_digits =
value_i >= 10000 ? (
value_i >= 1000000 ?
(value_i >= 100000000 ?
(value_i >= 1000000000 ? 10 : 9) :
(value_i >= 10000000 ? 8 : 7)) :
(value_i >= 100000 ? 6 : 5)
)
:
(
value_i >= 100 ?
(value_i >= 1000 ? 4 : 3) :
(value_i >= 10 ? 2 : 1)
);
// double precision is limited to about 17 decimal digits of meaningful values
if (prec_digits + value_digits > 17) prec_digits = 17-value_digits;
if (prec_digits > 9)
{
prec_digits2 = prec_digits - 9;
prec_digits = 9;
if (prec_digits2 > 9) prec_digits2 = 9;
}
const unsigned int prec_scale = scales[prec_digits-1];
const double dfrac = (value - value_i) * prec_scale;
if (prec_digits2 > 0)
{
const unsigned int prec_scale2 = scales[prec_digits2-1];
frac = (unsigned int) dfrac;
double dfrac2 = (dfrac - frac) * prec_scale2;
frac2 = (unsigned int) (dfrac2 + 0.5);
const int prec_scale2_small = wdl_min(prec_scale2/1024,10);
if (frac2 <= prec_scale2_small) frac2=0;
else if (frac2 >= prec_scale2 - prec_scale2_small - 1) frac2=prec_scale2;
if (frac2 >= prec_scale2)
{
frac2 -= prec_scale2;
frac++;
}
}
else
{
frac = (unsigned int) (dfrac + 0.5);
frac2 = 0;
const int prec_scale_small = wdl_min(prec_scale/1024,10);
if (frac <= prec_scale_small) frac=0;
else if (frac>=prec_scale-prec_scale_small - 1) frac=prec_scale;
}
if (frac >= prec_scale) // round up to next integer
{
frac -= prec_scale;
value_i++;
}
}
else // round to int
{
value_i = (unsigned int)(value+0.5);
frac2 = frac = 0;
}
char digs[32];
if (value_i)
{
int tmp=value_i;
int x = 0;
do {
const int a = (tmp%10);
tmp/=10;
digs[x++]='0' + a;
} while (tmp);
while (x>0) *bufOut++ = digs[--x];
}
else
{
*bufOut++ = '0';
}
if (frac || frac2)
{
int x = 0;
int tmp=frac;
int dleft = prec_digits;
*bufOut++='.';
if (frac) do
{
const int a = (tmp%10);
tmp /= 10;
if (x || a || frac2) digs[x++] = '0'+a;
} while (dleft-- > 0 && tmp);
while (dleft-->0) *bufOut++ = '0';
while (x>0) *bufOut++ = digs[--x];
// x is 0
if (frac2)
{
tmp=frac2;
dleft = prec_digits2;
do
{
const int a = (tmp%10);
tmp /= 10;
if (x || a) digs[x++] = '0'+a;
} while (dleft-- > 0 && tmp);
while (dleft-->0) *bufOut++ = '0';
while (x>0) *bufOut++ = digs[--x];
}
}
*bufOut = 0;
return bufOut;
}
int ProjectContextFormatString(char *outbuf, size_t outbuf_size, const char *fmt, va_list va)
{
int wroffs=0;
while (*fmt && outbuf_size > 1)
{
char c = *fmt++;
if (c != '%')
{
outbuf[wroffs++] = c != '\n' ? c : ' ';
outbuf_size--;
continue;
}
if (*fmt == '%')
{
outbuf[wroffs++] = '%';
outbuf_size--;
fmt++;
continue;
}
const char *ofmt = fmt-1;
bool want_abort=false;
int has_prec=0;
int prec=0;
if (*fmt == '.')
{
has_prec=1;
fmt++;
while (*fmt >= '0' && *fmt <= '9') prec = prec*10 + (*fmt++-'0');
if (*fmt != 'f' || prec < 0 || prec>20)
{
want_abort=true;
}
}
else if (*fmt == '0')
{
has_prec=2;
fmt++;
while (*fmt >= '0' && *fmt <= '9') prec = prec*10 + (*fmt++-'0');
if (*fmt != 'x' && *fmt != 'X' && *fmt != 'd' && *fmt != 'u')
{
want_abort=true;
}
}
c = *fmt++;
if (!want_abort) switch (c)
{
case '@':
case 'p':
case 's':
{
const char *str=va_arg(va,const char *);
bool prefer_quoteless = true;
if (c == 'p' && *fmt == '~') // %p~ to bias towards "string" (legacy)
{
prefer_quoteless = false;
fmt++;
}
const char qc = outbuf_size >= 3 && c != 's' ? getConfigStringQuoteChar(str, prefer_quoteless) : ' ';
if (qc != ' ')
{
outbuf[wroffs++] = qc ? qc : '`';
outbuf_size-=2; // will add trailing quote below
}
if (str) while (outbuf_size > 1 && *str)
{
char v = *str++;
if (!qc && v == '`') v = '\'';
outbuf[wroffs++] = v != '\n' && v != '\r' ? v : ' ';
outbuf_size--;
}
if (qc != ' ')
{
outbuf[wroffs++] = qc ? qc : '`';
// outbuf_size already decreased above
}
}
break;
case 'c':
{
int v = (va_arg(va,int)) & 0xff;
outbuf[wroffs++] = v != '\n' ? v : ' ';
outbuf_size--;
}
break;
case 'd':
{
int v = va_arg(va,int);
if (v<0)
{
outbuf[wroffs++] = '-';
outbuf_size--;
v=-v; // this won't handle -2147483648 right, todo special case?
}
char tab[32];
int x=0;
do
{
tab[x++] = v%10;
v/=10;
}
while (v);
if (has_prec == 2) while (x<prec) { tab[x++] = 0; }
while (--x >= 0 && outbuf_size>1)
{
outbuf[wroffs++] = '0' + tab[x];
outbuf_size--;
}
}
break;
case 'u':
{
unsigned int v = va_arg(va,unsigned int);
char tab[32];
int x=0;
do
{
tab[x++] = v%10;
v/=10;
}
while (v);
if (has_prec == 2) while (x<prec) { tab[x++] = 0; }
while (--x >= 0 && outbuf_size>1)
{
outbuf[wroffs++] = '0' + tab[x];
outbuf_size--;
}
}
break;
case 'x':
case 'X':
{
const char base = (c - 'x') + 'a';
unsigned int v = va_arg(va,unsigned int);
char tab[32];
int x=0;
do
{
tab[x++] = v&0xf;
v>>=4;
}
while (v);
if (has_prec == 2) while (x<prec) { tab[x++] = 0; }
while (--x >= 0 && outbuf_size>1)
{
if (tab[x] < 10)
outbuf[wroffs++] = '0' + tab[x];
else
outbuf[wroffs++] = base + tab[x] - 10;
outbuf_size--;
}
}
break;
case 'f':
{
double v = va_arg(va,double);
if (outbuf_size<64)
{
char tmp[64];
projectcontext_fastDoubleToString(v,tmp,has_prec?prec:6);
const char *str = tmp;
while (outbuf_size > 1 && *str)
{
outbuf[wroffs++] = *str++;
outbuf_size--;
}
}
else
{
const char *p=projectcontext_fastDoubleToString(v,outbuf+wroffs,has_prec?prec:6);
int amt = (int) (p-(outbuf+wroffs));
wroffs += amt;
outbuf_size-=amt;
}
}
break;
default:
want_abort=true;
break;
}
if (want_abort)
{
fmt=ofmt;
break;
}
}
outbuf += wroffs;
outbuf[0] = 0;
if (outbuf_size<2||!*fmt)
return wroffs;
#if defined(_WIN32) && defined(_MSC_VER)
// _vsnprintf() does not always null terminate (see below)
_vsnprintf(outbuf,outbuf_size,fmt,va);
#else
// vsnprintf() on non-win32, always null terminates
vsnprintf(outbuf,outbuf_size,fmt,va);
#endif
int l;
outbuf_size--;
for (l = 0; l < outbuf_size && outbuf[l]; l ++) if (outbuf[l] == '\n') outbuf[l] = ' ';
#if defined(_WIN32) && defined(_MSC_VER)
// nul terminate for _vsnprintf()
outbuf[l]=0;
#endif
return wroffs+l;
}
class ProjectStateContext_Mem : public ProjectStateContext
{
public:
ProjectStateContext_Mem(WDL_HeapBuf *hb, int rwflags)
{
m_rwflags=rwflags;
m_heapbuf=hb;
m_pos=0;
m_tmpflag=0;
#ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
memset(&m_compstream,0,sizeof(m_compstream));
m_usecomp=0;
#endif
}
virtual ~ProjectStateContext_Mem()
{
#ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
if (m_usecomp==1)
{
FlushComp(true);
deflateEnd(&m_compstream);
}
else if (m_usecomp==2)
{
inflateEnd(&m_compstream);
}
#endif
};
virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...);
virtual int GetLine(char *buf, int buflen); // returns -1 on eof
virtual WDL_INT64 GetOutputSize() { return m_heapbuf ? m_heapbuf->GetSize() : 0; }
virtual int GetTempFlag() { return m_tmpflag; }
virtual void SetTempFlag(int flag) { m_tmpflag=flag; }
int m_pos;
WDL_HeapBuf *m_heapbuf;
int m_tmpflag;
int m_rwflags;
#ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
int DecompressData()
{
if (m_pos >= m_heapbuf->GetSize()) return 1;
m_compstream.next_in = (unsigned char *)m_heapbuf->Get() + m_pos;
m_compstream.avail_in = m_heapbuf->GetSize()-m_pos;
m_compstream.total_in = 0;
int outchunk = 65536;
m_compstream.next_out = (unsigned char *)m_compdatabuf.Add(NULL,outchunk);
m_compstream.avail_out = outchunk;
m_compstream.total_out = 0;
int e = inflate(&m_compstream,Z_NO_FLUSH);
m_pos += m_compstream.total_in;
m_compdatabuf.Add(NULL,m_compstream.total_out - outchunk); // rewind
return e != Z_OK;
}
void FlushComp(bool eof)
{
while (m_compdatabuf.Available()>=WDL_MEMPROJECTCONTEXT_ZLIB_CHUNKSIZE || eof)
{
if (!m_heapbuf->GetSize()) m_heapbuf->SetGranul(256*1024);
m_compstream.next_in = (unsigned char *)m_compdatabuf.Get();
m_compstream.avail_in = m_compdatabuf.Available();
m_compstream.total_in = 0;
int osz = m_heapbuf->GetSize();
int newsz=osz + wdl_max(m_compstream.avail_in,8192) + 256;
m_compstream.next_out = (unsigned char *)m_heapbuf->Resize(newsz, false) + osz;
if (m_heapbuf->GetSize()!=newsz) return; // ERROR
m_compstream.avail_out = newsz-osz;
m_compstream.total_out=0;
deflate(&m_compstream,eof?Z_SYNC_FLUSH:Z_NO_FLUSH);
m_heapbuf->Resize(osz+m_compstream.total_out,false);
m_compdatabuf.Advance(m_compstream.total_in);
if (m_compstream.avail_out) break; // no need to process anymore
}
m_compdatabuf.Compact();
}
// these will be used for either decompression or compression, fear
int m_usecomp; // 0=?, -1 = fail, 1=yes
WDL_Queue m_compdatabuf;
z_stream m_compstream;
#endif
};
// returns length, modifies ptr to point to tmp if newline needed to be filtered
static int filter_newline_buf(const char **ptr, char *tmp, int tmpsz)
{
const char *use_buf = *ptr;
if (!use_buf) return -1;
int l;
for (l=0; use_buf[l] && use_buf[l] != '\n'; l++);
if (!use_buf[l]) return l;
lstrcpyn_safe(tmp,use_buf,tmpsz);
*ptr=tmp;
if (l >= tmpsz) return tmpsz-1;
for (;tmp[l]; l++) if (tmp[l] == '\n') tmp[l] = ' '; // replace any newlines with spaces
return l;
}
void ProjectStateContext_Mem::AddLine(const char *fmt, ...)
{
if (!m_heapbuf || !(m_rwflags&2)) return;
char tmp[8192];
const char *use_buf;
int l;
va_list va;
va_start(va,fmt);
if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2])
{
use_buf = va_arg(va,const char *);
l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp)) + 1;
}
else
{
l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va) + 1;
use_buf = tmp;
}
va_end(va);
if (l < 1) return;
#ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
if (!m_usecomp)
{
if (deflateInit(&m_compstream,WDL_MEMPROJECTCONTEXT_USE_ZLIB)==Z_OK) m_usecomp=1;
else m_usecomp=-1;
}
if (m_usecomp==1)
{
m_compdatabuf.Add(use_buf,(int)l);
FlushComp(false);
return;
}
#endif
const int sz=m_heapbuf->GetSize();
if (!sz && m_heapbuf->GetGranul() < 256*1024)
{
m_heapbuf->SetGranul(256*1024);
}
char *p = (char *)m_heapbuf->ResizeOK(sz+l);
if (!p)
{
// ERROR, resize to 0 and return
m_heapbuf->Resize(0);
m_heapbuf=0;
return;
}
memcpy(p+sz,use_buf,l);
}
int ProjectStateContext_Mem::GetLine(char *buf, int buflen) // returns -1 on eof
{
if (!m_heapbuf || !(m_rwflags&1)) return -1;
buf[0]=0;
#ifdef WDL_MEMPROJECTCONTEXT_USE_ZLIB
if (!m_usecomp)
{
unsigned char hdr[]={0x78,0x01};
if (m_heapbuf->GetSize()>2 && !memcmp(hdr,m_heapbuf->Get(),4) && inflateInit(&m_compstream)==Z_OK) m_usecomp=2;
else m_usecomp=-1;
}
if (m_usecomp==2)
{
int x=0;
for (;;)
{
const char *p = (const char*) m_compdatabuf.Get();
for (x = 0; x < m_compdatabuf.Available() && p[x] && p[x] != '\r' && p[x] != '\n'; x ++);
while (x >= m_compdatabuf.Available())
{
int err = DecompressData();
p = (const char *)m_compdatabuf.Get();
for (; x < m_compdatabuf.Available() && p[x] && p[x] != '\r' && p[x] != '\n'; x ++);
if (err) break;
}
if (x||!m_compdatabuf.Available()) break;
m_compdatabuf.Advance(1); // skip over nul or newline char
}
if (!x) return -1;
if (buflen > 0 && buf)
{
int l = wdl_min(buflen-1,x);
memcpy(buf,m_compdatabuf.Get(),l);
buf[l]=0;
}
m_compdatabuf.Advance(x+1);
m_compdatabuf.Compact();
return 0;
}
#endif
int avail = m_heapbuf->GetSize() - m_pos;
const char *p=(const char *)m_heapbuf->Get() + m_pos;
while (avail > 0 && (p[0] =='\r'||p[0]=='\n'||!p[0]||p[0] == ' ' || p[0] == '\t'))
{
p++;
m_pos++;
avail--;
}
if (avail <= 0) return -1;
int x;
for (x = 0; x < avail && p[x] && p[x] != '\n'; x ++);
m_pos += x+1;
if (buflen > 0&&buf)
{
int l = buflen-1;
if (l>x) l=x;
memcpy(buf,p,l);
if (l>0 && buf[l-1]=='\r') l--;
buf[l]=0;
}
return 0;
}
class ProjectStateContext_File : public ProjectStateContext
{
public:
ProjectStateContext_File(WDL_FileRead *rd, WDL_FileWrite *wr)
{
m_rd=rd;
m_wr=wr;
m_indent=0;
m_bytesOut=0;
m_errcnt=false;
m_tmpflag=0;
_rdbuf_pos = _rdbuf_valid = 0;
}
virtual ~ProjectStateContext_File(){ delete m_rd; delete m_wr; };
virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...);
virtual int GetLine(char *buf, int buflen); // returns -1 on eof
virtual WDL_INT64 GetOutputSize() { return m_bytesOut; }
virtual int GetTempFlag() { return m_tmpflag; }
virtual void SetTempFlag(int flag) { m_tmpflag=flag; }
bool HasError() { return m_errcnt; }
WDL_INT64 m_bytesOut WDL_FIXALIGN;
WDL_FileRead *m_rd;
WDL_FileWrite *m_wr;
char rdbuf[4096];
int _rdbuf_pos, _rdbuf_valid;
int m_indent;
int m_tmpflag;
bool m_errcnt;
};
int ProjectStateContext_File::GetLine(char *buf, int buflen)
{
if (!m_rd||buflen<3) return -1;
char * const buf_orig=buf;
int rdpos = _rdbuf_pos;
int rdvalid = _rdbuf_valid;
buflen -= 2;
for (;;)
{
while (rdpos < rdvalid)
{
char c=rdbuf[rdpos++];
switch (c)
{
case ' ': case '\r': case '\n': case '\t': break;
default:
*buf++=c;
do
{
int mxl = rdvalid - rdpos;
if (mxl > buflen) mxl=buflen;
while (mxl-->0)
{
char c2 = rdbuf[rdpos++];
if (c2=='\n') goto finished;
*buf++ = c2;
buflen--;
}
if (rdpos>=rdvalid)
{
rdpos = 0;
rdvalid = m_rd->Read(rdbuf, sizeof(rdbuf));
if (rdvalid<1) break;
}
}
while (buflen > 0);
finished:
_rdbuf_pos=rdpos;
_rdbuf_valid=rdvalid;
if (buf > buf_orig && buf[-1] == '\r') buf--;
*buf=0;
return 0;
}
}
rdpos = 0;
rdvalid = m_rd->Read(rdbuf, sizeof(rdbuf));
if (rdvalid<1)
{
buf[0]=0;
return -1;
}
}
}
void ProjectStateContext_File::AddLine(const char *fmt, ...)
{
if (m_wr && !m_errcnt)
{
int err=0;
char tmp[8192];
const char *use_buf;
va_list va;
va_start(va,fmt);
int l;
if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2])
{
// special case "%s" passed, directly use it
use_buf = va_arg(va,const char *);
l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp));
}
else
{
l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va);
use_buf = tmp;
}
va_end(va);
if (l < 0) return;
int a=m_indent;
if (use_buf[0] == '<') m_indent+=2;
else if (use_buf[0] == '>') a=(m_indent-=2);
if (a>0)
{
m_bytesOut+=a;
char tb[128];
memset(tb,' ',a < (int)sizeof(tb) ? a : (int)sizeof(tb));
while (a>0)
{
const int tl = a < (int)sizeof(tb) ? a : (int)sizeof(tb);
a-=tl;
m_wr->Write(tb,tl);
}
}
err |= m_wr->Write(use_buf,l) != l;
err |= m_wr->Write("\r\n",2) != 2;
m_bytesOut += 2 + l;
if (err) m_errcnt=true;
}
}
ProjectStateContext *ProjectCreateFileRead(const char *fn)
{
WDL_FileRead *rd = new WDL_FileRead(fn,0,65536,1);
if (!rd || !rd->IsOpen())
{
delete rd;
return NULL;
}
return new ProjectStateContext_File(rd,NULL);
}
ProjectStateContext *ProjectCreateFileWrite(const char *fn)
{
WDL_FileWrite *wr = new WDL_FileWrite(fn);
if (!wr || !wr->IsOpen())
{
delete wr;
return NULL;
}
return new ProjectStateContext_File(NULL,wr);
}
ProjectStateContext *ProjectCreateMemCtx(WDL_HeapBuf *hb)
{
return new ProjectStateContext_Mem(hb,3);
}
ProjectStateContext *ProjectCreateMemCtx_Read(const WDL_HeapBuf *hb)
{
return new ProjectStateContext_Mem((WDL_HeapBuf *)hb,1);
}
ProjectStateContext *ProjectCreateMemCtx_Write(WDL_HeapBuf *hb)
{
return new ProjectStateContext_Mem(hb,2);
}
class ProjectStateContext_FastQueue : public ProjectStateContext
{
public:
ProjectStateContext_FastQueue(WDL_FastQueue *fq)
{
m_fq = fq;
m_tmpflag=0;
}
virtual ~ProjectStateContext_FastQueue()
{
};
virtual void WDL_VARARG_WARN(printf,2,3) AddLine(const char *fmt, ...);
virtual int GetLine(char *buf, int buflen) { return -1; }//unsup
virtual WDL_INT64 GetOutputSize() { return m_fq ? m_fq->Available() : 0; }
virtual int GetTempFlag() { return m_tmpflag; }
virtual void SetTempFlag(int flag) { m_tmpflag=flag; }
WDL_FastQueue *m_fq;
int m_tmpflag;
};
void ProjectStateContext_FastQueue::AddLine(const char *fmt, ...)
{
if (!m_fq) return;
va_list va;
va_start(va,fmt);
char tmp[8192];
if (fmt && fmt[0] == '%' && (fmt[1] == 's' || fmt[1] == 'S') && !fmt[2])
{
const char *use_buf = va_arg(va,const char *);
const int l = filter_newline_buf(&use_buf,tmp,(int)sizeof(tmp));
if (use_buf) m_fq->Add(use_buf, l + 1);
}
else
{
const int l = ProjectContextFormatString(tmp,sizeof(tmp),fmt, va);
if (l>0) m_fq->Add(tmp, l+1);
}
va_end(va);
}
ProjectStateContext *ProjectCreateMemWriteFastQueue(WDL_FastQueue *fq) // only write!
{
return new ProjectStateContext_FastQueue(fq);
}
bool ProjectContext_GetNextLine(ProjectStateContext *ctx, LineParser *lpOut)
{
for (;;)
{
char linebuf[4096];
if (ctx->GetLine(linebuf,sizeof(linebuf)))
{
lpOut->parse("");
return false;
}
if (lpOut->parse(linebuf)||lpOut->getnumtokens()<=0) continue;
return true; // success!
}
}
bool ProjectContext_EatCurrentBlock(ProjectStateContext *ctx, ProjectStateContext *ctxOut)
{
int child_count=1;
if (ctx) for (;;)
{
char linebuf[4096];
if (ctx->GetLine(linebuf,sizeof(linebuf))) break;
const char *sp = linebuf;
while (*sp == ' ' || *sp == '\t') sp++;
const char *p = sp;
if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any
if (p[0] == '>') if (--child_count < 1) return true;
if (ctxOut) ctxOut->AddLine("%s",sp);
if (p[0] == '<') child_count++;
}
return false;
}
#include "wdl_base64.h"
int cfg_decode_binary(ProjectStateContext *ctx, WDL_HeapBuf *hb) // 0 on success, doesnt clear hb
{
int child_count=1;
for (;;)
{
char linebuf[4096];
if (ctx->GetLine(linebuf,sizeof(linebuf))) break;
const char *p = linebuf;
while (*p == ' ' || *p == '\t') p++;
if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any
if (p[0] == '<') child_count++;
else if (p[0] == '>') { if (child_count-- == 1) return 0; }
else if (child_count == 1 && p[0])
{
unsigned char buf[3200];
const int buf_l=wdl_base64decode(p,buf,sizeof(buf));
if (buf_l)
{
const int os=hb->GetSize();
char *dest = (char*)hb->ResizeOK(os+buf_l);
if (dest) memcpy(dest+os,buf,buf_l);
}
}
}
return -1;
}
void cfg_encode_binary(ProjectStateContext *ctx, const void *ptr, int len)
{
if (!ctx || len < 1) return;
const unsigned char *p=(const unsigned char *)ptr;
if (len > 128 && len < (1<<30))
{
// we could (probably should) use dynamic_cast<> here, but as we span modules this
// raises all kinds of questions (especially with VC having the option to disable RTTI).
// for now, we assume that the first void * in an object is the vtable pointer. with
// testing, of course.
WDL_FastQueue *fq = NULL;
WDL_HeapBuf *hb = NULL;
#ifndef WDL_MEMPROJECTCONTEXT_USE_ZLIB
static const ProjectStateContext_Mem hb_def(NULL,0);
#endif
static const ProjectStateContext_FastQueue fq_def(NULL);
if (*(void **)ctx == *(void **)&fq_def)
{
fq=((ProjectStateContext_FastQueue*)ctx)->m_fq;
}
#ifndef WDL_MEMPROJECTCONTEXT_USE_ZLIB
else if (*(void **)ctx == *(void **)&hb_def)
{
hb=((ProjectStateContext_Mem*)ctx)->m_heapbuf;
}
#endif
if (fq||hb)
{
const int linelen8 = 280/8;
const int enc_len = ((len+2)/3)*4; // every 3 characters end up as 4
const int lines = (enc_len + linelen8*8 - 1) / (linelen8*8);
char *wr = NULL;
if (fq)
{
wr = (char*)fq->Add(WDL_FASTQUEUE_ADD_NOZEROBUF,enc_len + lines);
}
else if (hb)
{
const int oldsz=hb->GetSize();
wr=(char*)hb->ResizeOK(oldsz + enc_len + lines,false);
if (wr) wr+=oldsz;
}
if (wr)
{
#ifdef _DEBUG
char * const wr_end=wr + enc_len + lines;
#endif
int lpos = 0;
while (len >= 6)
{
const int accum = (p[0] << 16) + (p[1] << 8) + p[2];
const int accum2 = (p[3] << 16) + (p[4] << 8) + p[5];
wr[0] = wdl_base64_alphabet[(accum >> 18) & 0x3F];
wr[1] = wdl_base64_alphabet[(accum >> 12) & 0x3F];
wr[2] = wdl_base64_alphabet[(accum >> 6) & 0x3F];
wr[3] = wdl_base64_alphabet[accum & 0x3F];
wr[4] = wdl_base64_alphabet[(accum2 >> 18) & 0x3F];
wr[5] = wdl_base64_alphabet[(accum2 >> 12) & 0x3F];
wr[6] = wdl_base64_alphabet[(accum2 >> 6) & 0x3F];
wr[7] = wdl_base64_alphabet[accum2 & 0x3F];
wr+=8;
p+=6;
len-=6;
if (++lpos >= linelen8) { *wr++= 0; lpos=0; }
}
if (len >= 3)
{
const int accum = (p[0] << 16) + (p[1] << 8) + p[2];
wr[0] = wdl_base64_alphabet[(accum >> 18) & 0x3F];
wr[1] = wdl_base64_alphabet[(accum >> 12) & 0x3F];
wr[2] = wdl_base64_alphabet[(accum >> 6) & 0x3F];
wr[3] = wdl_base64_alphabet[accum & 0x3F];
wr+=4;
p+=3;
len-=3;
lpos+=3;
}
if (len>0)
{
lpos += len;
if (len == 2)
{
const int accum = (p[0] << 8) | p[1];
wr[0] = wdl_base64_alphabet[(accum >> 10) & 0x3F];
wr[1] = wdl_base64_alphabet[(accum >> 4) & 0x3F];
wr[2] = wdl_base64_alphabet[(accum & 0xF)<<2];
}
else
{
const int accum = p[0];
wr[0] = wdl_base64_alphabet[(accum >> 2) & 0x3F];
wr[1] = wdl_base64_alphabet[(accum & 0x3)<<4];
wr[2] = '=';
}
wr[3] = '=';
wr+=4;
}
if (lpos>0) *wr++=0;
#ifdef _DEBUG
if (wr != wr_end) wdl_log("cfg_encode_binary: block mode size mismatch %d!\n", (int)(wr-wr_end));
#endif
return;
}
}
}
do
{
char buf[256];
int thiss=len;
if (thiss > 96) thiss=96;
wdl_base64encode(p,buf,thiss);
ctx->AddLine("%s",buf);
p+=thiss;
len-=thiss;
}
while (len>0);
}
int cfg_decode_textblock(ProjectStateContext *ctx, WDL_FastString *str) // 0 on success, appends to str
{
int child_count=1;
bool did_firstline=!!str->Get()[0];
for (;;)
{
char linebuf[4096];
if (ctx->GetLine(linebuf,sizeof(linebuf))) break;
const char *p = linebuf;
while (*p == ' ' || *p == '\t') p++;
if (*p == '\'' || *p == '"' || *p == '`') p++; // skip a quote if any
if (!p[0]) continue;
else if (p[0] == '<') child_count++;
else if (p[0] == '>') { if (child_count-- == 1) return 0; }
else if (child_count == 1)
{
const char *prefix = did_firstline ? "\r\n" : "";
// lines can have a prefix immediately before | to specify the line ending of the previous line
switch (p[0])
{
case 'c': prefix=""; p++; break;
case 'n': prefix="\n"; p++; break;
case 'r': prefix="\r"; p++; break;
case 'R': prefix="\n\r"; p++; break;
}
if (p[0] == '|')
{
if (*prefix) str->Append(prefix);
str->Append(++p);
did_firstline=true;
}
}
}
return -1;
}
void cfg_encode_textblock(ProjectStateContext *ctx, const char *txt) // splits long lines
{
while (*txt)
{
int l = 0;
while (txt[l] && l < 4000 && txt[l] != '\r' && txt[l] != '\n') l++;
ctx->AddLine("|%.*s",l,txt);
txt += l;
if (*txt == '\r')
{
if (*++txt== '\n') txt++;
}
else if (*txt == '\n')
{
if (*++txt == '\r') txt++;
}
}
}
void cfg_encode_textblock2(ProjectStateContext *ctx, const char *txt) // preserves long lines, line endings
{
char prefix = ' ';
while (*txt)
{
int l = 0;
while (txt[l] && l < 2000 && txt[l] != '\r' && txt[l] != '\n') l++;
ctx->AddLine("%c|%.*s",prefix,l,txt);
txt += l;
if (*txt == '\r')
{
prefix = 'r';
if (*++txt== '\n')
{
txt++;
prefix = ' ';
}
}
else if (*txt == '\n')
{
prefix = 'n';
if (*++txt == '\r')
{
txt++;
prefix = 'R';
}
}
else if (*txt) prefix = 'c';
else break;
if (!*txt)
ctx->AddLine("%c|",prefix);
}
}
char getConfigStringQuoteChar(const char *p, bool prefer_quoteless)
{
if (!p || !*p) return '"';
char fc = *p;
int flags=0;
while (*p && flags!=15)
{
char c=*p++;
if (c=='"') flags|=1;
else if (c=='\'') flags|=2;
else if (c=='`') flags|=4;
else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') flags |= 8;
}
if (prefer_quoteless || flags == 7)
{
if (!(flags & 8) && fc != '"' && fc != '\'' && fc != '`' && fc != '#' && fc != ';') return ' ';
}
if (!(flags & 1)) return '"';
if (!(flags & 2)) return '\'';
if (!(flags & 4)) return '`';
return 0;
}
bool configStringWantsBlockEncoding(const char *in) // returns true if over 1k long, has newlines, or contains all quote chars
{
int maxl = 1024, flags = 0;
while (--maxl)
{
switch (*in++)
{
case 0: return false;
case '\n': return true;
case '"': if ((flags|=1)==7) return true; break;
case '`': if ((flags|=2)==7) return true; break;
case '\'': if ((flags|=4)==7) return true; break;
}
}
return true;
}
void makeEscapedConfigString(const char *in, WDL_FastString *out)
{
char c;
if (!in || !*in) out->Set("\"\"");
else if ((c = getConfigStringQuoteChar(in)))
{
if (c == ' ')
{
out->Set(in);
}
else
{
out->Set(&c,1);
out->Append(in);
out->Append(&c,1);
}
}
else // ick, change ` into '
{
out->Set("`");
out->Append(in);
out->Append("`");
char *p=(char *)out->Get()+1;
while (*p && p[1])
{
if (*p == '`') *p='\'';
else if (*p == '\r' || *p == '\n') *p=' ';
p++;
}
}
}