mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-04-21 19:45:56 +03:00
634 lines
19 KiB
C++
634 lines
19 KiB
C++
/* Copyright (C) 2014 InfiniDB, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; version 2 of
|
|
the 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. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
/*
|
|
* $Id: dictionary.cpp 2122 2013-07-08 16:33:50Z bpaul $
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <boost/scoped_array.hpp>
|
|
#include <boost/algorithm/string/trim.hpp>
|
|
#include <sys/types.h>
|
|
using namespace std;
|
|
|
|
#include "primitiveprocessor.h"
|
|
#include "we_type.h"
|
|
#include "messagelog.h"
|
|
#include "messageobj.h"
|
|
#include "exceptclasses.h"
|
|
#include "dataconvert.h"
|
|
#include "string_prefixes.h"
|
|
#include <sstream>
|
|
|
|
using namespace logging;
|
|
|
|
const char* nullString = " "; // this is not NULL to preempt segfaults.
|
|
const int nullStringLen = 0;
|
|
|
|
namespace
|
|
{
|
|
const char* signatureNotFound = joblist::CPSTRNOTFOUND.c_str();
|
|
}
|
|
|
|
namespace primitives
|
|
{
|
|
inline bool PrimitiveProcessor::compare(const datatypes::Charset& cs, uint8_t COP, const char* str1,
|
|
size_t length1, const char* str2, size_t length2) throw()
|
|
{
|
|
int error = 0;
|
|
bool rc = primitives::StringComparator(cs).op(&error, COP, ConstString(str1, length1),
|
|
ConstString(str2, length2));
|
|
if (error)
|
|
{
|
|
MessageLog logger(LoggingID(28));
|
|
logging::Message::Args colWidth;
|
|
Message msg(34);
|
|
|
|
colWidth.add(COP);
|
|
colWidth.add("compare");
|
|
msg.format(colWidth);
|
|
logger.logErrorMessage(msg);
|
|
return false; // throw an exception here?
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
Notes:
|
|
- assumes no continuation pointer
|
|
*/
|
|
|
|
void PrimitiveProcessor::p_TokenByScan(const TokenByScanRequestHeader* h, TokenByScanResultHeader* ret,
|
|
unsigned outSize, boost::shared_ptr<DictEqualityFilter> eqFilter)
|
|
{
|
|
const DataValue* args;
|
|
const uint8_t* niceBlock; // block cast to a byte-indexed type
|
|
const uint8_t* niceInput; // h cast to a byte-indexed type
|
|
const uint16_t* offsets;
|
|
int offsetIndex, argsOffset;
|
|
bool cmpResult = false;
|
|
int i;
|
|
const char* sig;
|
|
uint16_t siglen;
|
|
|
|
PrimToken* retTokens;
|
|
DataValue* retDataValues;
|
|
int rdvOffset;
|
|
uint8_t* niceRet; // ret cast to a byte-indexed type
|
|
|
|
// set up pointers to fields within each structure
|
|
|
|
// either retTokens or retDataValues will be used but not both.
|
|
niceRet = reinterpret_cast<uint8_t*>(ret);
|
|
rdvOffset = sizeof(TokenByScanResultHeader);
|
|
|
|
retTokens = reinterpret_cast<PrimToken*>(&niceRet[rdvOffset]);
|
|
retDataValues = reinterpret_cast<DataValue*>(&niceRet[rdvOffset]);
|
|
|
|
{
|
|
void* retp = static_cast<void*>(ret);
|
|
memcpy(retp, h, sizeof(PrimitiveHeader) + sizeof(ISMPacketHeader));
|
|
}
|
|
ret->NVALS = 0;
|
|
ret->NBYTES = sizeof(TokenByScanResultHeader);
|
|
ret->ism.Command = DICT_SCAN_COMPARE_RESULTS;
|
|
|
|
//...Initialize I/O counts
|
|
ret->CacheIO = 0;
|
|
ret->PhysicalIO = 0;
|
|
|
|
niceBlock = reinterpret_cast<const uint8_t*>(block);
|
|
offsets = reinterpret_cast<const uint16_t*>(&niceBlock[10]);
|
|
niceInput = reinterpret_cast<const uint8_t*>(h);
|
|
|
|
const datatypes::Charset cs(h->charsetNumber);
|
|
|
|
for (offsetIndex = 1; offsets[offsetIndex] != 0xffff; offsetIndex++)
|
|
{
|
|
siglen = offsets[offsetIndex - 1] - offsets[offsetIndex];
|
|
sig = reinterpret_cast<const char*>(&niceBlock[offsets[offsetIndex]]);
|
|
argsOffset = sizeof(TokenByScanRequestHeader);
|
|
args = reinterpret_cast<const DataValue*>(&niceInput[argsOffset]);
|
|
|
|
if (eqFilter)
|
|
{
|
|
if (&cs.getCharset() != &eqFilter->getCharset())
|
|
{
|
|
// throw runtime_error("Collations mismatch: TokenByScanRequestHeader and DicEqualityFilter");
|
|
}
|
|
string strData(sig, siglen);
|
|
bool gotIt = eqFilter->find(strData) != eqFilter->end();
|
|
|
|
if ((h->COP1 == COMPARE_EQ && gotIt) || (h->COP1 == COMPARE_NE && !gotIt))
|
|
goto store;
|
|
|
|
goto no_store;
|
|
}
|
|
|
|
cmpResult = compare(cs, h->COP1, sig, siglen, args->data, args->len);
|
|
|
|
switch (h->NVALS)
|
|
{
|
|
case 1:
|
|
{
|
|
if (cmpResult)
|
|
goto store;
|
|
|
|
goto no_store;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
if (!cmpResult && h->BOP == BOP_AND)
|
|
goto no_store;
|
|
|
|
if (cmpResult && h->BOP == BOP_OR)
|
|
goto store;
|
|
|
|
argsOffset += sizeof(uint16_t) + args->len;
|
|
args = (DataValue*)&niceInput[argsOffset];
|
|
|
|
cmpResult = compare(cs, h->COP2, sig, siglen, args->data, args->len);
|
|
|
|
if (cmpResult)
|
|
goto store;
|
|
|
|
goto no_store;
|
|
}
|
|
|
|
default:
|
|
{
|
|
idbassert(0);
|
|
for (i = 0, cmpResult = true; i < h->NVALS; i++)
|
|
{
|
|
cmpResult = compare(cs, h->COP1, sig, siglen, args->data, args->len);
|
|
|
|
if (!cmpResult && h->BOP == BOP_AND)
|
|
goto no_store;
|
|
|
|
if (cmpResult && h->BOP == BOP_OR)
|
|
goto store;
|
|
|
|
argsOffset += sizeof(uint16_t) + args->len;
|
|
args = (DataValue*)&niceInput[argsOffset];
|
|
}
|
|
|
|
if (i == h->NVALS && cmpResult)
|
|
goto store;
|
|
else
|
|
goto no_store;
|
|
}
|
|
}
|
|
|
|
store:
|
|
|
|
if (h->OutputType == OT_DATAVALUE)
|
|
{
|
|
if ((ret->NBYTES + sizeof(DataValue) + siglen) > outSize)
|
|
{
|
|
MessageLog logger(LoggingID(28));
|
|
logging::Message::Args marker;
|
|
Message msg(35);
|
|
|
|
marker.add(8);
|
|
msg.format(marker);
|
|
logger.logErrorMessage(msg);
|
|
|
|
throw logging::DictionaryBufferOverflow();
|
|
}
|
|
|
|
retDataValues->len = siglen;
|
|
memcpy(retDataValues->data, sig, siglen);
|
|
rdvOffset += sizeof(DataValue) + siglen;
|
|
retDataValues = (DataValue*)&niceRet[rdvOffset];
|
|
ret->NVALS++;
|
|
ret->NBYTES += sizeof(DataValue) + siglen;
|
|
}
|
|
else if (h->OutputType == OT_TOKEN)
|
|
{
|
|
if ((ret->NBYTES + sizeof(PrimToken)) > outSize)
|
|
{
|
|
MessageLog logger(LoggingID(28));
|
|
logging::Message::Args marker;
|
|
Message msg(35);
|
|
|
|
marker.add(9);
|
|
msg.format(marker);
|
|
logger.logErrorMessage(msg);
|
|
|
|
throw logging::DictionaryBufferOverflow();
|
|
}
|
|
|
|
retTokens[ret->NVALS].LBID = h->LBID;
|
|
retTokens[ret->NVALS].offset = offsetIndex; // need index rather than the block offset... rp 12/19/06
|
|
retTokens[ret->NVALS].len = args->len;
|
|
ret->NVALS++;
|
|
ret->NBYTES += sizeof(PrimToken);
|
|
}
|
|
/*
|
|
* XXXPAT: HACK! Ron requested a special case where the input string
|
|
* that matched and the token of the matched string were returned.
|
|
* It will not be used in cases where there are multiple input strings.
|
|
* We need to rethink the requirements for this primitive after Dec 15.
|
|
*/
|
|
else if (h->OutputType == OT_BOTH)
|
|
{
|
|
if (ret->NBYTES + sizeof(PrimToken) + sizeof(DataValue) + args->len > outSize)
|
|
{
|
|
MessageLog logger(LoggingID(28));
|
|
logging::Message::Args marker;
|
|
Message msg(35);
|
|
|
|
marker.add(10);
|
|
msg.format(marker);
|
|
logger.logErrorMessage(msg);
|
|
|
|
throw logging::DictionaryBufferOverflow();
|
|
}
|
|
|
|
retDataValues->len = args->len;
|
|
memcpy(retDataValues->data, args->data, args->len);
|
|
rdvOffset += sizeof(DataValue) + args->len;
|
|
retTokens = reinterpret_cast<PrimToken*>(&niceRet[rdvOffset]);
|
|
retTokens->LBID = h->LBID;
|
|
retTokens->offset = offsetIndex; // need index rather than the block offset... rp 12/19/06
|
|
retTokens->len = args->len;
|
|
rdvOffset += sizeof(PrimToken);
|
|
retDataValues = reinterpret_cast<DataValue*>(&niceRet[rdvOffset]);
|
|
ret->NBYTES += sizeof(PrimToken) + sizeof(DataValue) + args->len;
|
|
ret->NVALS++;
|
|
}
|
|
|
|
no_store:; // this is intentional
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void PrimitiveProcessor::nextSig(int NVALS, const PrimToken* tokens, p_DataValue* ret, uint8_t outputFlags,
|
|
bool oldGetSigBehavior, bool skipNulls) throw()
|
|
{
|
|
const uint8_t* niceBlock = reinterpret_cast<const uint8_t*>(block);
|
|
const uint16_t* offsets = reinterpret_cast<const uint16_t*>(&niceBlock[10]);
|
|
|
|
if (NVALS == 0)
|
|
{
|
|
if (offsets[dict_OffsetIndex + 1] == 0xffff)
|
|
{
|
|
ret->len = -1;
|
|
return;
|
|
}
|
|
|
|
ret->len = offsets[dict_OffsetIndex] - offsets[dict_OffsetIndex + 1];
|
|
ret->data = &niceBlock[offsets[dict_OffsetIndex + 1]];
|
|
|
|
if (outputFlags & OT_TOKEN)
|
|
currentOffsetIndex = dict_OffsetIndex + 1;
|
|
}
|
|
else
|
|
{
|
|
again:
|
|
|
|
if (dict_OffsetIndex >= NVALS)
|
|
{
|
|
ret->len = -1;
|
|
return;
|
|
}
|
|
|
|
if (oldGetSigBehavior)
|
|
{
|
|
const OldGetSigParams* oldParams = reinterpret_cast<const OldGetSigParams*>(tokens);
|
|
|
|
if (oldParams[dict_OffsetIndex].rid & 0x8000000000000000LL)
|
|
{
|
|
if (skipNulls)
|
|
{
|
|
/* Bug 3321. For some cases the NULL token should be skipped. The
|
|
* isnull filter is handled by token columncommand or by the F & E
|
|
* framework. This primitive should only process nulls
|
|
* when it's for projection.
|
|
*/
|
|
dict_OffsetIndex++;
|
|
goto again;
|
|
}
|
|
|
|
ret->len = nullStringLen;
|
|
ret->data = (const uint8_t*)nullString;
|
|
}
|
|
else
|
|
{
|
|
ret->len = offsets[oldParams[dict_OffsetIndex].offsetIndex - 1] -
|
|
offsets[oldParams[dict_OffsetIndex].offsetIndex];
|
|
// Whoa! apparently we have come across a missing signature! That is, the requested ordinal
|
|
// is larger than the number of signatures in this block. Return a "special" string so that
|
|
// the query keeps going, but that can be recognized as an internal error upon inspection.
|
|
//@Bug 2534. Change the length check to 8000
|
|
|
|
// MCOL-267:
|
|
// With BLOB support we have had to increase this to 8176
|
|
// because a BLOB can take 8176 bytes of a dictionary block
|
|
// instead of the fixed 8000 with CHAR/VARCHAR
|
|
if (ret->len < 0 || ret->len > 8176)
|
|
{
|
|
ret->data = reinterpret_cast<const uint8_t*>(signatureNotFound);
|
|
ret->len = strlen(reinterpret_cast<const char*>(ret->data));
|
|
}
|
|
else
|
|
ret->data = &niceBlock[offsets[oldParams[dict_OffsetIndex].offsetIndex]];
|
|
}
|
|
|
|
// idbassert(ret->len >= 0);
|
|
currentOffsetIndex = oldParams[dict_OffsetIndex].offsetIndex;
|
|
dict_OffsetIndex++;
|
|
return;
|
|
}
|
|
|
|
/* XXXPAT: Need to check for the NULL token here */
|
|
ret->len = tokens[dict_OffsetIndex].len;
|
|
ret->data = &niceBlock[tokens[dict_OffsetIndex].offset];
|
|
|
|
if (outputFlags & OT_TOKEN)
|
|
{
|
|
// offsets = reinterpret_cast<const uint16_t *>(&niceBlock[10]);
|
|
for (currentOffsetIndex = 1; offsets[currentOffsetIndex] != 0xffff; currentOffsetIndex++)
|
|
if (tokens[dict_OffsetIndex].offset == offsets[currentOffsetIndex])
|
|
break;
|
|
|
|
if (offsets[currentOffsetIndex] == 0xffff)
|
|
{
|
|
MessageLog logger(LoggingID(28));
|
|
logging::Message::Args offset;
|
|
Message msg(38);
|
|
|
|
offset.add(tokens[dict_OffsetIndex].offset);
|
|
msg.format(offset);
|
|
logger.logErrorMessage(msg);
|
|
|
|
currentOffsetIndex = -1;
|
|
dict_OffsetIndex++;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
dict_OffsetIndex++;
|
|
}
|
|
|
|
void PrimitiveProcessor::p_Dictionary(const DictInput* in, vector<uint8_t>* out, bool skipNulls,
|
|
#if defined(XXX_PRIMITIVES_TOKEN_RANGES_XXX)
|
|
uint32_t charsetNumber, boost::shared_ptr<DictEqualityFilter> eqFilter,
|
|
uint8_t eqOp, uint64_t minMax[2])
|
|
#else
|
|
uint32_t charsetNumber, boost::shared_ptr<DictEqualityFilter> eqFilter,
|
|
uint8_t eqOp)
|
|
#endif
|
|
{
|
|
PrimToken* outToken;
|
|
const DictFilterElement* filter = 0;
|
|
const uint8_t* in8;
|
|
DataValue* outValue;
|
|
p_DataValue min = {0, NULL}, max = {0, NULL}, sigptr = {0, NULL};
|
|
int tmp, filterIndex, filterOffset;
|
|
uint16_t aggCount;
|
|
bool cmpResult;
|
|
DictOutput header;
|
|
const datatypes::Charset& cs(charsetNumber);
|
|
|
|
// default size of the ouput to something sufficiently large to prevent
|
|
// excessive reallocation and copy when resizing
|
|
const unsigned DEF_OUTSIZE = 16 * 1024;
|
|
// use this factor to scale out size of future resize calls
|
|
const int SCALE_FACTOR = 2;
|
|
out->resize(DEF_OUTSIZE);
|
|
|
|
in8 = reinterpret_cast<const uint8_t*>(in);
|
|
|
|
{
|
|
void* hp = static_cast<void*>(&header);
|
|
memcpy(hp, in, sizeof(ISMPacketHeader) + sizeof(PrimitiveHeader));
|
|
}
|
|
header.ism.Command = DICT_RESULTS;
|
|
header.NVALS = 0;
|
|
header.LBID = in->LBID;
|
|
dict_OffsetIndex = 0;
|
|
filterIndex = 0;
|
|
aggCount = 0;
|
|
min.len = 0;
|
|
max.len = 0;
|
|
|
|
//...Initialize I/O counts
|
|
header.CacheIO = 0;
|
|
header.PhysicalIO = 0;
|
|
|
|
header.NBYTES = sizeof(DictOutput);
|
|
|
|
for (nextSig(in->NVALS, in->tokens, &sigptr, in->OutputType, (in->InputFlags ? true : false), skipNulls);
|
|
sigptr.len != -1;
|
|
nextSig(in->NVALS, in->tokens, &sigptr, in->OutputType, (in->InputFlags ? true : false), skipNulls))
|
|
{
|
|
#if defined(XXX_PRIMITIVES_TOKEN_RANGES_XXX)
|
|
if (minMax)
|
|
{
|
|
uint64_t v = encodeStringPrefix_check_null(sigptr.data, sigptr.len, charsetNumber);
|
|
minMax[1] = minMax[1] < v ? v : minMax[1];
|
|
minMax[0] = minMax[0] > v ? v : minMax[0];
|
|
}
|
|
#endif
|
|
// do aggregate processing
|
|
if (in->OutputType & OT_AGGREGATE)
|
|
{
|
|
// len == 0 indicates this is the first pass
|
|
if (max.len != 0)
|
|
{
|
|
tmp = cs.strnncollsp(sigptr.data, sigptr.len, max.data, max.len);
|
|
|
|
if (tmp > 0)
|
|
max = sigptr;
|
|
}
|
|
else
|
|
max = sigptr;
|
|
|
|
if (min.len != 0)
|
|
{
|
|
tmp = cs.strnncollsp(sigptr.data, sigptr.len, min.data, min.len);
|
|
|
|
if (tmp < 0)
|
|
min = sigptr;
|
|
}
|
|
else
|
|
min = sigptr;
|
|
|
|
aggCount++;
|
|
}
|
|
|
|
// filter processing
|
|
if (in->InputFlags == 1)
|
|
filterOffset = sizeof(DictInput) + (in->NVALS * sizeof(OldGetSigParams));
|
|
else
|
|
filterOffset = sizeof(DictInput) + (in->NVALS * sizeof(PrimToken));
|
|
|
|
if (eqFilter)
|
|
{
|
|
// MCOL-4407.
|
|
// Support filters:
|
|
// `where key = value0 and key = value1 {and key = value2}`
|
|
//
|
|
// The problem occurs only when HWM > columnstore_string_scan_threshold - 1
|
|
// because in this case:
|
|
// CS uses `tryCombineDictionary` which combines `DictStep`s into one as result
|
|
// `eqFilter` has more than 1 filter and applies the logic below which is `or` by
|
|
// default.
|
|
// Note: The case HWM <= columnstore_string_scan_threshold - 1 has the same problem,
|
|
// function `p_TokenByScan`, but it does not occur because `tryCombineDictionaryScan`
|
|
// was turned off.
|
|
if (eqFilter->size() > 1 && in->BOP == BOP_AND && eqOp == COMPARE_EQ)
|
|
goto no_store;
|
|
|
|
if (eqFilter->size() > 1 && in->BOP == BOP_OR && eqOp == COMPARE_NE)
|
|
goto store;
|
|
|
|
// MCOL-1246 Trim whitespace before match
|
|
string strData((char*)sigptr.data, sigptr.len);
|
|
boost::trim_right_if(strData, boost::is_any_of(" "));
|
|
bool gotIt = eqFilter->find(strData) != eqFilter->end();
|
|
|
|
if ((gotIt && eqOp == COMPARE_EQ) || (!gotIt && eqOp == COMPARE_NE))
|
|
goto store;
|
|
|
|
goto no_store;
|
|
}
|
|
|
|
for (filterIndex = 0; filterIndex < in->NOPS; filterIndex++)
|
|
{
|
|
filter = reinterpret_cast<const DictFilterElement*>(&in8[filterOffset]);
|
|
|
|
cmpResult = compare(cs, filter->COP, (const char*)sigptr.data, sigptr.len, (const char*)filter->data,
|
|
filter->len);
|
|
|
|
if (!cmpResult && in->BOP != BOP_OR)
|
|
goto no_store;
|
|
|
|
if (cmpResult && in->BOP != BOP_AND)
|
|
goto store;
|
|
|
|
filterOffset += sizeof(DictFilterElement) + filter->len;
|
|
}
|
|
|
|
if (filterIndex == in->NOPS && in->BOP != BOP_OR)
|
|
{
|
|
store:
|
|
// cout << "storing it, str = " << string((char *)sigptr.data, sigptr.len) << endl;
|
|
header.NVALS++;
|
|
|
|
if (in->OutputType & OT_RID && in->InputFlags == 1) // hack that indicates old GetSignature behavior
|
|
{
|
|
const OldGetSigParams* oldParams;
|
|
uint64_t* outRid;
|
|
oldParams = reinterpret_cast<const OldGetSigParams*>(in->tokens);
|
|
uint32_t newlen = header.NBYTES + 8;
|
|
|
|
if (newlen > out->size())
|
|
{
|
|
out->resize(out->size() * SCALE_FACTOR);
|
|
}
|
|
|
|
outRid = (uint64_t*)&(*out)[header.NBYTES];
|
|
// mask off the upper bit of the rid; signifies the NULL token was passed in
|
|
*outRid = (oldParams[dict_OffsetIndex - 1].rid & 0x7fffffffffffffffLL);
|
|
header.NBYTES += 8;
|
|
}
|
|
|
|
if (in->OutputType & OT_INPUTARG && in->InputFlags == 0)
|
|
{
|
|
uint32_t newlen = header.NBYTES + sizeof(DataValue) + filter->len;
|
|
|
|
if (newlen > out->size())
|
|
{
|
|
out->resize(out->size() * SCALE_FACTOR);
|
|
}
|
|
|
|
outValue = reinterpret_cast<DataValue*>(&(*out)[header.NBYTES]);
|
|
outValue->len = filter->len;
|
|
memcpy(outValue->data, filter->data, filter->len);
|
|
header.NBYTES += sizeof(DataValue) + filter->len;
|
|
}
|
|
|
|
if (in->OutputType & OT_TOKEN)
|
|
{
|
|
uint32_t newlen = header.NBYTES + sizeof(PrimToken);
|
|
|
|
if (newlen > out->size())
|
|
{
|
|
out->resize(out->size() * SCALE_FACTOR);
|
|
}
|
|
|
|
outToken = reinterpret_cast<PrimToken*>(&(*out)[header.NBYTES]);
|
|
outToken->LBID = in->LBID;
|
|
outToken->offset = currentOffsetIndex;
|
|
outToken->len = filter->len;
|
|
header.NBYTES += sizeof(PrimToken);
|
|
}
|
|
|
|
if (in->OutputType & OT_DATAVALUE)
|
|
{
|
|
uint32_t newlen = header.NBYTES + sizeof(DataValue) + sigptr.len;
|
|
|
|
if (newlen > out->size())
|
|
{
|
|
out->resize(out->size() * SCALE_FACTOR);
|
|
}
|
|
|
|
outValue = reinterpret_cast<DataValue*>(&(*out)[header.NBYTES]);
|
|
outValue->len = sigptr.len;
|
|
memcpy(outValue->data, sigptr.data, sigptr.len);
|
|
header.NBYTES += sizeof(DataValue) + sigptr.len;
|
|
}
|
|
}
|
|
|
|
no_store:; // intentional
|
|
}
|
|
|
|
if (in->OutputType & OT_AGGREGATE)
|
|
{
|
|
uint32_t newlen = header.NBYTES + 3 * sizeof(uint16_t) + min.len + max.len;
|
|
|
|
if (newlen > out->size())
|
|
{
|
|
out->resize(out->size() * SCALE_FACTOR);
|
|
}
|
|
|
|
uint16_t* tmp16 = reinterpret_cast<uint16_t*>(&(*out)[header.NBYTES]);
|
|
DataValue* tmpDV = reinterpret_cast<DataValue*>(&(*out)[header.NBYTES + sizeof(uint16_t)]);
|
|
|
|
*tmp16 = aggCount;
|
|
tmpDV->len = min.len;
|
|
memcpy(tmpDV->data, min.data, min.len);
|
|
header.NBYTES += 2 * sizeof(uint16_t) + min.len;
|
|
|
|
tmpDV = reinterpret_cast<DataValue*>(&(*out)[header.NBYTES]);
|
|
tmpDV->len = max.len;
|
|
memcpy(tmpDV->data, max.data, max.len);
|
|
header.NBYTES += sizeof(uint16_t) + max.len;
|
|
}
|
|
|
|
out->resize(header.NBYTES);
|
|
|
|
memcpy(&(*out)[0], &header, sizeof(DictOutput));
|
|
}
|
|
|
|
} // namespace primitives
|