1
0
Fork 0

added selectivity estimates for some index types

This commit is contained in:
Jan Steemann 2015-01-30 23:04:46 +01:00
parent 436ac1adfc
commit 39603ebaf4
15 changed files with 342 additions and 44 deletions

View File

@ -1,6 +1,26 @@
v2.5.0 (XXXX-XX-XX)
-------------------
* added selectivity estimates for primary, edge, and hash indexes.
The selectivity estimates are returned by the `GET /_api/index` REST API method
in a sub-attribute `selectivityEstimate` for each index that supports it. This
attribute will be omitted for indexes that do not provide selectivity estimates.
If provided, the selectivity estimate will be a numeric value between 0 and 1.
Selectivity estimates will also be reported in the result of `collection.getIndexes()`
for all indexes that support this. If no selectivity estimate can be determined for
an index, the attribute `selectivityEstimate` will be omitted here, too.
The web interface also shows selectivity estimates for each index that supports this.
Currently the following index types can provide selectivity estimates:
- primary index
- edge index
- hash index (unique and non-unique)
No selectivity estimates will be provided when running in cluster mode.
* fixed issue #1226: arangod log issues
* added additional logger if arangod is started in foreground mode on a tty

View File

@ -4,7 +4,6 @@ require 'rspec'
require 'arangodb.rb'
describe ArangoDB do
api = "/_api/index"
prefix = "api-index-hash"
################################################################################

View File

@ -457,6 +457,7 @@ SHELL_COMMON = \
@top_srcdir@/js/common/tests/shell-cap-constraint-timecritical.js \
@top_srcdir@/js/common/tests/shell-unique-constraint.js \
@top_srcdir@/js/common/tests/shell-hash-index.js \
@top_srcdir@/js/common/tests/shell-hash-index-noncluster.js \
@top_srcdir@/js/common/tests/shell-fulltext.js \
@top_srcdir@/js/common/tests/shell-graph.js

View File

@ -115,25 +115,23 @@ namespace triagens {
}
bool hasSelectivityEstimate () const {
if (type == TRI_IDX_TYPE_PRIMARY_INDEX ||
type == TRI_IDX_TYPE_HASH_INDEX) {
return true;
if (! hasInternals()) {
return false;
}
return false;
return getInternals()->_hasSelectivityEstimate;
}
double selectivityEstimate () const {
TRI_ASSERT_EXPENSIVE(hasSelectivityEstimate());
TRI_index_t* internals = getInternals();
if (type == TRI_IDX_TYPE_PRIMARY_INDEX) {
return 1.0;
}
if (type == TRI_IDX_TYPE_HASH_INDEX) {
return TRI_SelectivityHashIndex(getInternals());
}
TRI_ASSERT(internals->_hasSelectivityEstimate);
TRI_ASSERT(false);
return internals->selectivityEstimate(internals);
}
inline bool hasInternals () const {
return (internals != nullptr);
}
TRI_index_t* getInternals () const {

View File

@ -761,7 +761,7 @@ double TRI_SelectivityHashArrayMulti (TRI_hash_array_multi_t* array) {
size_t numTotal = array->_nrUsed + array->_nrOverflowUsed;
if (numTotal == 0) {
return -1.0;
return 1.0;
}
return static_cast<double>(array->_nrUsed) / static_cast<double>(numTotal);
}

View File

@ -593,6 +593,20 @@ static int SizeHintHashIndex (TRI_index_t* idx,
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief the hash index can provide a selectivity estimate
////////////////////////////////////////////////////////////////////////////////
static double SelectivityEstimateHashIndex (TRI_index_t const* idx) {
TRI_hash_index_t* hashIndex = (TRI_hash_index_t*) idx;
if (hashIndex->base._unique) {
return 1.0;
}
return TRI_SelectivityHashArrayMulti(&hashIndex->_hashArrayMulti);
}
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
@ -615,11 +629,13 @@ TRI_index_t* TRI_CreateHashIndex (TRI_document_collection_t* document,
TRI_InitIndex(idx, iid, TRI_IDX_TYPE_HASH_INDEX, document, unique, false);
idx->memory = MemoryHashIndex;
idx->json = JsonHashIndex;
idx->insert = InsertHashIndex;
idx->remove = RemoveHashIndex;
idx->sizeHint = SizeHintHashIndex;
idx->_hasSelectivityEstimate = true;
idx->selectivityEstimate = SelectivityEstimateHashIndex;
idx->memory = MemoryHashIndex;
idx->json = JsonHashIndex;
idx->insert = InsertHashIndex;
idx->remove = RemoveHashIndex;
idx->sizeHint = SizeHintHashIndex;
// ...........................................................................
// Copy the contents of the path list vector into a new vector and store this

View File

@ -1299,7 +1299,7 @@ static void DropIndexCoordinator (const v8::FunctionCallbackInfo<v8::Value>& arg
/// [
/// { "id" : "example/0", "type" : "primary", "fields" : ["_id"] },
/// { "id" : "example/991154", "unique" : false, "type" : "skiplist", "fields" : ["a", "b"] }
/// ]
/// ]
///
/// arango> db.example.dropIndex(i[0])
/// false
@ -1308,7 +1308,9 @@ static void DropIndexCoordinator (const v8::FunctionCallbackInfo<v8::Value>& arg
/// true
///
/// arango> i = db.example.getIndexes();
/// [{ "id" : "example/0", "type" : "primary", "fields" : ["_id"] }]
/// [
/// { "id" : "example/0", "type" : "primary", "fields" : ["_id"] }
/// ]
/// ```
///
/// @endDocuBlock
@ -1422,7 +1424,7 @@ static void GetIndexesCoordinator (const v8::FunctionCallbackInfo<v8::Value>& ar
/// @startDocuBlock collectionGetIndexes
/// `getIndexes()`
///
/// Returns a list of all indexes defined for the collection.
/// Returns an array of all indexes defined for the collection.
///
/// @EXAMPLES
///

View File

@ -90,13 +90,15 @@ void TRI_InitIndex (TRI_index_t* idx,
idx->_collection = document;
idx->_unique = unique;
idx->_sparse = sparse;
idx->_hasSelectivityEstimate = false;
// init common functions
idx->memory = nullptr;
idx->removeIndex = nullptr;
idx->cleanup = nullptr;
idx->sizeHint = nullptr;
idx->postInsert = nullptr;
idx->selectivityEstimate = nullptr;
idx->memory = nullptr;
idx->removeIndex = nullptr;
idx->cleanup = nullptr;
idx->sizeHint = nullptr;
idx->postInsert = nullptr;
LOG_TRACE("initialising index of type %s", TRI_TypeNameIndex(idx->_type));
}
@ -433,6 +435,9 @@ TRI_json_t* TRI_JsonIndex (TRI_memory_zone_t* zone,
TRI_Insert3ObjectJson(zone, json, "id", TRI_CreateStringCopyJson(zone, number, strlen(number)));
TRI_Insert3ObjectJson(zone, json, "type", TRI_CreateStringCopyJson(zone, TRI_TypeNameIndex(idx->_type), strlen(TRI_TypeNameIndex(idx->_type))));
TRI_Insert3ObjectJson(zone, json, "unique", TRI_CreateBooleanJson(zone, idx->_unique));
if (idx->_hasSelectivityEstimate) {
TRI_Insert3ObjectJson(zone, json, "selectivityEstimate", TRI_CreateNumberJson(zone, idx->selectivityEstimate(idx)));
}
TRI_FreeString(TRI_CORE_MEM_ZONE, number);
}
@ -525,6 +530,14 @@ static size_t MemoryPrimary (TRI_index_t const* idx) {
return idx->_collection->_primaryIndex._nrAlloc * sizeof(void*);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the selectivity estimate for the index
////////////////////////////////////////////////////////////////////////////////
static double SelectivityEstimatePrimary (TRI_index_t const* idx) {
return 1.0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief JSON description of a primary index
////////////////////////////////////////////////////////////////////////////////
@ -565,10 +578,12 @@ TRI_index_t* TRI_CreatePrimaryIndex (TRI_document_collection_t* document) {
TRI_InitIndex(idx, 0, TRI_IDX_TYPE_PRIMARY_INDEX, document, true, false);
idx->memory = MemoryPrimary;
idx->json = JsonPrimary;
idx->insert = InsertPrimary;
idx->remove = RemovePrimary;
idx->_hasSelectivityEstimate = true;
idx->selectivityEstimate = &SelectivityEstimatePrimary;
idx->memory = MemoryPrimary;
idx->json = JsonPrimary;
idx->insert = InsertPrimary;
idx->remove = RemovePrimary;
return idx;
}
@ -920,6 +935,16 @@ static size_t MemoryEdge (TRI_index_t const* idx) {
TRI_MemoryUsageMultiPointer(&(((TRI_edge_index_t*) idx)->_edges_to));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a selectivity esimtate for the index
////////////////////////////////////////////////////////////////////////////////
static double SelectivityEstimateEdge (TRI_index_t const* idx) {
// return average selectivity of the two index parts
return (TRI_SelectivityEstimateMultiPointer(&(((TRI_edge_index_t*) idx)->_edges_from)) +
TRI_SelectivityEstimateMultiPointer(&(((TRI_edge_index_t*) idx)->_edges_to))) * 0.5;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief JSON description of edge index
////////////////////////////////////////////////////////////////////////////////
@ -1028,10 +1053,12 @@ TRI_index_t* TRI_CreateEdgeIndex (TRI_document_collection_t* document,
TRI_InitIndex(idx, iid, TRI_IDX_TYPE_EDGE_INDEX, document, false, false);
idx->memory = MemoryEdge;
idx->json = JsonEdge;
idx->insert = InsertEdge;
idx->remove = RemoveEdge;
idx->_hasSelectivityEstimate = true;
idx->selectivityEstimate = SelectivityEstimateEdge;
idx->memory = MemoryEdge;
idx->json = JsonEdge;
idx->insert = InsertEdge;
idx->remove = RemoveEdge;
idx->sizeHint = SizeHintEdge;

View File

@ -101,7 +101,9 @@ typedef struct TRI_index_s {
bool _unique;
bool _ignoreNull;
bool _sparse;
bool _hasSelectivityEstimate;
double (*selectivityEstimate) (struct TRI_index_s const*);
size_t (*memory) (struct TRI_index_s const*);
TRI_json_t* (*json) (struct TRI_index_s const*);
void (*removeIndex) (struct TRI_index_s*, struct TRI_document_collection_t*);

View File

@ -230,12 +230,13 @@
<th class="collectionInfoTh">ID</th>
<th class="collectionInfoTh">Type</th>
<th class="collectionInfoTh">Unique</th>
<th class="collectionInfoTh">Selectivity</th>
<th class="collectionInfoTh">Fields</th>
</tr>
<%
if (index){
var fieldString = '';
$.each(index.indexes, function(k,v) {
$.each(index.indexes, function(k, v) {
if (v.fields !== undefined) {
fieldString = v.fields.join(", ");
}
@ -243,11 +244,17 @@
//cut index id
var position = v.id.indexOf('/');
var indexId = v.id.substr(position+1, v.id.length);
var selectivity = (
v.hasOwnProperty("selectivityEstimate") ?
(v.selectivityEstimate * 100).toFixed(1) + "%" :
"n/a"
);
%>
<tr>
<th class="collectionInfoTh modal-text"><%=indexId%></th>
<th class="collectionInfoTh modal-text"><%=v.type%></th>
<th class="collectionInfoTh modal-text"><%=v.unique%></th>
<th class="collectionInfoTh modal-text"><%=selectivity%></th>
<th class="collectionInfoTh modal-text"><%=fieldString%></th>
</tr>
<%

View File

@ -222,10 +222,10 @@
}
if(collectionIsLoaded) {
if (collectionIsLoaded) {
// prevent "unexpected sync method error"
var wfs = this.model.getProperties().waitForSync;
tableContent.push(
var wfs = this.model.getProperties().waitForSync;
tableContent.push(
window.modalView.createSelectEntry(
"change-collection-sync",
"Wait for sync",
@ -263,7 +263,7 @@
this.truncateCollection.bind(this)
)
);
if(collectionIsLoaded) {
if (collectionIsLoaded) {
buttons.push(
window.modalView.createNotificationButton(
"Unload",

View File

@ -1095,7 +1095,7 @@
var fieldString = '';
var actionString = '';
$.each(this.index.indexes, function(k,v) {
$.each(this.index.indexes, function(k, v) {
if (v.type === 'primary' || v.type === 'edge') {
actionString = '<span class="icon_arangodb_locked" ' +
'data-original-title="No action"></span>';
@ -1111,13 +1111,19 @@
//cut index id
var position = v.id.indexOf('/');
var indexId = v.id.substr(position+1, v.id.length);
var indexId = v.id.substr(position + 1, v.id.length);
var selectivity = (
v.hasOwnProperty("selectivityEstimate") ?
(v.selectivityEstimate * 100).toFixed(1) + "%" :
"n/a"
);
$('#collectionEditIndexTable').append(
'<tr>'+
'<th class=' + JSON.stringify(cssClass) + '>' + indexId + '</th>'+
'<th class=' + JSON.stringify(cssClass) + '>' + v.type + '</th>'+
'<th class=' + JSON.stringify(cssClass) + '>' + v.unique + '</th>'+
'<th class=' + JSON.stringify(cssClass) + '>' + selectivity + '</th>'+
'<th class=' + JSON.stringify(cssClass) + '>' + fieldString + '</th>'+
'<th class=' + JSON.stringify(cssClass) + '>' + actionString + '</th>'+
'</tr>'

View File

@ -0,0 +1,189 @@
/*global require, db, assertEqual, assertTrue */
////////////////////////////////////////////////////////////////////////////////
/// @brief test the hash index, selectivity estimates
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var internal = require("internal");
// -----------------------------------------------------------------------------
// --SECTION-- basic methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: Creation
////////////////////////////////////////////////////////////////////////////////
function HashIndexSuite() {
var cn = "UnitTestsCollectionHash";
var collection = null;
var sorter = function (l, r) {
if (l.length != r.length) {
return l.length - r.length < 0 ? -1 : 1;
}
// length is equal
for (i = 0; i < l.length; ++i) {
if (l[i] != r[i]) {
return l[i] < r[i] ? -1 : 1;
}
}
return 0;
};
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
internal.db._drop(cn);
collection = internal.db._create(cn, { waitForSync : false });
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
// try...catch is necessary as some tests delete the collection itself!
try {
collection.unload();
collection.drop();
}
catch (err) {
}
collection = null;
internal.wait(0.0);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief hash index selectivity
////////////////////////////////////////////////////////////////////////////////
testSelectivityEstimateUnique : function () {
var i;
var idx = collection.ensureUniqueConstraint("value");
for (i = 0; i < 1000; ++i) {
collection.save({ _key: "test" + i, value: i });
}
idx = collection.ensureUniqueConstraint("value");
assertEqual(1, idx.selectivityEstimate);
for (i = 0; i < 50; ++i) {
collection.remove("test" + i);
}
idx = collection.ensureUniqueConstraint("value");
assertEqual(1, idx.selectivityEstimate);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief multi hash index selectivity
////////////////////////////////////////////////////////////////////////////////
testSelectivityEstimateNonUnique : function () {
var i;
var idx = collection.ensureHashIndex("value");
for (i = 0; i < 1000; ++i) {
collection.save({ value: i });
}
idx = collection.ensureHashIndex("value");
assertEqual(1, idx.selectivityEstimate);
for (i = 0; i < 1000; ++i) {
collection.save({ value: i });
}
idx = collection.ensureHashIndex("value");
assertTrue(idx.selectivityEstimate >= 0.45 && idx.selectivityEstimate <= 0.55);
for (i = 0; i < 1000; ++i) {
collection.save({ value: i });
}
idx = collection.ensureHashIndex("value");
assertTrue(idx.selectivityEstimate >= 0.3 && idx.selectivityEstimate <= 0.36);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief multi hash index selectivity
////////////////////////////////////////////////////////////////////////////////
testSelectivityEstimateAllIdentical : function () {
var i;
var idx = collection.ensureHashIndex("value");
for (i = 0; i < 1000; ++i) {
collection.save({ value: 1 });
}
idx = collection.ensureHashIndex("value");
assertTrue(idx.selectivityEstimate <= (1 / 1000 + 0.0001));
for (i = 0; i < 1000; ++i) {
collection.save({ value: 1 });
}
idx = collection.ensureHashIndex("value");
assertTrue(idx.selectivityEstimate <= (2 / 2000 + 0.0001));
for (i = 0; i < 1000; ++i) {
collection.save({ value: 1 });
}
idx = collection.ensureHashIndex("value");
assertTrue(idx.selectivityEstimate <= (2 / 3000 + 0.0001));
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suites
////////////////////////////////////////////////////////////////////////////////
jsunity.run(HashIndexSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -78,6 +78,8 @@ int TRI_InitMultiPointer (TRI_multi_pointer_t* array,
array->_memoryZone = zone;
array->_nrUsed = 0;
array->_nrAlloc = 0;
array->_nrUnique = 0;
array->_nrDuplicate = 0;
if (nullptr == (array->_table_alloc = static_cast<TRI_multi_pointer_entry_t*>(TRI_Allocate(zone,
sizeof(TRI_multi_pointer_entry_t) * INITIAL_SIZE + 64, true)))) {
@ -374,6 +376,19 @@ size_t TRI_MemoryUsageMultiPointer (TRI_multi_pointer_t const* array) {
return (size_t) array->_nrAlloc * sizeof(TRI_multi_pointer_entry_t) + 64;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a selectivity estimate of the index
////////////////////////////////////////////////////////////////////////////////
double TRI_SelectivityEstimateMultiPointer (TRI_multi_pointer_t const* array) {
size_t numTotal = array->_nrUnique + array->_nrDuplicate;
if (numTotal == 0) {
return 1.0;
}
return static_cast<double>(array->_nrUnique) / static_cast<double>(numTotal);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief adds a key/element to the array
////////////////////////////////////////////////////////////////////////////////
@ -416,6 +431,7 @@ void* TRI_InsertElementMultiPointer (TRI_multi_pointer_t* array,
array->_table[i].next = TRI_MULTI_POINTER_INVALID_INDEX;
array->_table[i].prev = TRI_MULTI_POINTER_INVALID_INDEX;
array->_nrUsed++;
array->_nrUnique++;
#ifdef TRI_CHECK_MULTI_POINTER_HASH
TRI_CheckMultiPointerHash(array, true, true);
#endif
@ -442,6 +458,7 @@ void* TRI_InsertElementMultiPointer (TRI_multi_pointer_t* array,
array->_table[i].next = TRI_MULTI_POINTER_INVALID_INDEX;
array->_table[i].prev = TRI_MULTI_POINTER_INVALID_INDEX;
array->_nrUsed++;
array->_nrUnique++;
#ifdef TRI_CHECK_MULTI_POINTER_HASH
TRI_CheckMultiPointerHash(array, true, true);
#endif
@ -489,6 +506,7 @@ void* TRI_InsertElementMultiPointer (TRI_multi_pointer_t* array,
array->_table[array->_table[j].next].prev = j;
}
array->_nrUsed++;
array->_nrDuplicate++;
#ifdef TRI_CHECK_MULTI_POINTER_HASH
TRI_CheckMultiPointerHash(array, true, true);
@ -596,6 +614,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t* array, void const* ele
TRI_CheckMultiPointerHash(array, false, false);
#endif
HealHole(array, i);
array->_nrUnique--;
}
else {
// There is at least one successor in position j.
@ -605,6 +624,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t* array, void const* ele
TRI_CheckMultiPointerHash(array, false, false);
#endif
HealHole(array, j);
array->_nrDuplicate--;
}
}
else {
@ -621,6 +641,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t* array, void const* ele
TRI_CheckMultiPointerHash(array, false, false);
#endif
HealHole(array, i);
array->_nrDuplicate--;
}
array->_nrUsed--;
#ifdef TRI_CHECK_MULTI_POINTER_HASH
@ -679,11 +700,11 @@ static int ResizeMultiPointer (TRI_multi_pointer_t* array, size_t size) {
////////////////////////////////////////////////////////////////////////////////
int TRI_ResizeMultiPointer (TRI_multi_pointer_t* array, size_t size) {
if (2*size+1 < array->_nrUsed) {
if (2 * size + 1 < array->_nrUsed) {
return TRI_ERROR_BAD_PARAMETER;
}
return ResizeMultiPointer(array, 2*size+1);
return ResizeMultiPointer(array, 2 * size + 1);
}
// -----------------------------------------------------------------------------

View File

@ -112,6 +112,8 @@ typedef struct TRI_multi_pointer_s {
uint64_t _nrAlloc; // the size of the table
uint64_t _nrUsed; // the number of used entries
uint64_t _nrUnique; // number of unique entries
uint64_t _nrDuplicate; // number of duplicate entries
TRI_multi_pointer_entry_t* _table_alloc; // the table itself
TRI_multi_pointer_entry_t* _table; // the table itself, 64 aligned
@ -175,6 +177,12 @@ void TRI_FreeMultiPointer (TRI_memory_zone_t*, TRI_multi_pointer_t*);
size_t TRI_MemoryUsageMultiPointer (TRI_multi_pointer_t const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief return a selectivity estimate for the index
////////////////////////////////////////////////////////////////////////////////
double TRI_SelectivityEstimateMultiPointer (TRI_multi_pointer_t const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief lookups an element given a key
////////////////////////////////////////////////////////////////////////////////
@ -211,6 +219,7 @@ void* TRI_RemoveElementMultiPointer (TRI_multi_pointer_t*, void const* element);
int TRI_ResizeMultiPointer (TRI_multi_pointer_t*, size_t);
#if 0
// -----------------------------------------------------------------------------
// --SECTION-- MULTI ASSOCIATIVE POINTERS WITH MULTIPLE KEYS
// -----------------------------------------------------------------------------
@ -393,6 +402,7 @@ void* TRI_RemovePairMultiPair (TRI_multi_pair_t*,
////////////////////////////////////////////////////////////////////////////////
int TRI_ResizeMultiPair (TRI_multi_pair_t*, size_t);
#endif
#endif